@autohq/cli 0.1.133 → 0.1.135
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/dist/agent-bridge.js +37 -12
- package/dist/index.js +779 -160
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -334,8 +334,8 @@ async function createOAuthLoopbackCallback(input) {
|
|
|
334
334
|
});
|
|
335
335
|
let resolveResult;
|
|
336
336
|
let rejectResult;
|
|
337
|
-
const result = new Promise((
|
|
338
|
-
resolveResult =
|
|
337
|
+
const result = new Promise((resolve3, reject) => {
|
|
338
|
+
resolveResult = resolve3;
|
|
339
339
|
rejectResult = reject;
|
|
340
340
|
});
|
|
341
341
|
const server = createServer((request, response) => {
|
|
@@ -595,14 +595,14 @@ async function listenOnPreferredPort(server, port) {
|
|
|
595
595
|
}
|
|
596
596
|
}
|
|
597
597
|
async function listen(server, port) {
|
|
598
|
-
await new Promise((
|
|
598
|
+
await new Promise((resolve3, reject) => {
|
|
599
599
|
const onError = (error51) => {
|
|
600
600
|
server.off("listening", onListening);
|
|
601
601
|
reject(error51);
|
|
602
602
|
};
|
|
603
603
|
const onListening = () => {
|
|
604
604
|
server.off("error", onError);
|
|
605
|
-
|
|
605
|
+
resolve3();
|
|
606
606
|
};
|
|
607
607
|
server.once("error", onError);
|
|
608
608
|
server.once("listening", onListening);
|
|
@@ -873,7 +873,7 @@ function persistLogin(config2, input) {
|
|
|
873
873
|
}
|
|
874
874
|
}
|
|
875
875
|
async function sleep(ms) {
|
|
876
|
-
await new Promise((
|
|
876
|
+
await new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
877
877
|
}
|
|
878
878
|
var init_login = __esm({
|
|
879
879
|
"src/commands/auth/login.ts"() {
|
|
@@ -18053,7 +18053,7 @@ function isChatMessageEvent(trigger) {
|
|
|
18053
18053
|
function hasFilterValue(trigger, path2, expected) {
|
|
18054
18054
|
return trigger.where?.[path2] === expected;
|
|
18055
18055
|
}
|
|
18056
|
-
var RESOURCE_KIND_SESSION, SESSION_HARNESSES, TriggerFilterScalarSchema, TriggerFilterPathSchema, TriggerFilterClauseSchema, TriggerFilterSchema, SessionTriggerCheckTimeoutSchema, TriggerEventSchema, TriggerEventsSchema, SessionTriggerSharedFields, SessionTriggerEventSourceFields, SessionTriggerBaseSchema, SessionTriggerDefinitionBaseSchema, SessionTriggerSchema, SessionApplyTriggerSchema, SessionHeartbeatTriggerBaseSchema, SessionHeartbeatTriggerSchema, SessionApplyHeartbeatTriggerSchema, SessionTriggerDefinitionSchema, SessionApplyTriggerDefinitionSchema, SessionTriggersSchema, SessionApplyTriggersSchema, AVATAR_ASSET_EXTENSIONS, MAX_AVATAR_ASSET_BYTES, MIN_AVATAR_ASSET_DIMENSION_PX, MAX_AVATAR_ASSET_DIMENSION_PX, PNG_SIGNATURE, SESSION_IDENTITY_DESCRIPTION_MAX_LENGTH, SHA256_HEX_PATTERN, SessionIdentitySchema, SessionSpecFieldsSchema, SessionSpecSchema, SessionApplySpecSchema, SessionStatusSchema, SessionResourceSchema, SessionApplyRequestSchema, SessionApplyTriggerReceiptSchema, SessionApplyResponseSchema, SESSION_TELEGRAM_IDENTITY_STATUSES, SessionTelegramIdentityStatusSchema, SessionPresenceIdentitySchema, SessionPresenceResponseSchema, SessionPresenceConnectRequestSchema, SessionPresenceConnectPendingSchema, SessionPresenceConnectResponseSchema, SessionPresenceIconRequestSchema, SessionPresenceIconResponseSchema, SessionPresenceCompleteResponseSchema;
|
|
18056
|
+
var RESOURCE_KIND_AGENT, LEGACY_RESOURCE_KIND_SESSION, RESOURCE_KIND_SESSION, SESSION_HARNESSES, TriggerFilterScalarSchema, TriggerFilterPathSchema, TriggerFilterClauseSchema, TriggerFilterSchema, SessionTriggerCheckTimeoutSchema, TriggerEventSchema, TriggerEventsSchema, SessionTriggerSharedFields, SessionTriggerEventSourceFields, SessionTriggerBaseSchema, SessionTriggerDefinitionBaseSchema, SessionTriggerSchema, SessionApplyTriggerSchema, SessionHeartbeatTriggerBaseSchema, SessionHeartbeatTriggerSchema, SessionApplyHeartbeatTriggerSchema, SessionTriggerDefinitionSchema, SessionApplyTriggerDefinitionSchema, SessionTriggersSchema, SessionApplyTriggersSchema, AVATAR_ASSET_EXTENSIONS, MAX_AVATAR_ASSET_BYTES, MIN_AVATAR_ASSET_DIMENSION_PX, MAX_AVATAR_ASSET_DIMENSION_PX, PNG_SIGNATURE, SESSION_IDENTITY_DESCRIPTION_MAX_LENGTH, SHA256_HEX_PATTERN, SessionIdentitySchema, SessionSpecFieldsSchema, SessionSpecSchema, SessionApplySpecSchema, SessionStatusSchema, SessionResourceSchema, SessionApplyRequestSchema, SessionApplyTriggerReceiptSchema, SessionApplyResponseSchema, SESSION_TELEGRAM_IDENTITY_STATUSES, SessionTelegramIdentityStatusSchema, SessionPresenceIdentitySchema, SessionPresenceResponseSchema, SessionPresenceConnectRequestSchema, SessionPresenceConnectPendingSchema, SessionPresenceConnectResponseSchema, SessionPresenceIconRequestSchema, SessionPresenceIconResponseSchema, SessionPresenceCompleteResponseSchema;
|
|
18057
18057
|
var init_sessions = __esm({
|
|
18058
18058
|
"../../packages/schemas/src/sessions.ts"() {
|
|
18059
18059
|
"use strict";
|
|
@@ -18065,7 +18065,9 @@ var init_sessions = __esm({
|
|
|
18065
18065
|
init_secrets();
|
|
18066
18066
|
init_tools();
|
|
18067
18067
|
init_trigger_router();
|
|
18068
|
-
|
|
18068
|
+
RESOURCE_KIND_AGENT = "agent";
|
|
18069
|
+
LEGACY_RESOURCE_KIND_SESSION = "session";
|
|
18070
|
+
RESOURCE_KIND_SESSION = RESOURCE_KIND_AGENT;
|
|
18069
18071
|
SESSION_HARNESSES = ["claude-code"];
|
|
18070
18072
|
TriggerFilterScalarSchema = external_exports.union([
|
|
18071
18073
|
external_exports.null(),
|
|
@@ -18339,7 +18341,7 @@ var init_sessions = __esm({
|
|
|
18339
18341
|
/**
|
|
18340
18342
|
* Scopes the current agent-app scope set requests that this realized
|
|
18341
18343
|
* identity's install never granted. Computed at read time; non-empty means
|
|
18342
|
-
* the install predates a scope addition and `auto
|
|
18344
|
+
* the install predates a scope addition and `auto agents connect
|
|
18343
18345
|
* <session> --reconnect` refreshes it.
|
|
18344
18346
|
*/
|
|
18345
18347
|
missingScopes: external_exports.array(external_exports.string().trim().min(1)).optional()
|
|
@@ -18584,7 +18586,16 @@ var init_project_service_accounts = __esm({
|
|
|
18584
18586
|
});
|
|
18585
18587
|
|
|
18586
18588
|
// ../../packages/schemas/src/project-resources.ts
|
|
18587
|
-
|
|
18589
|
+
function normalizeLegacySessionKind(value) {
|
|
18590
|
+
if (isRecord(value) && value.kind === LEGACY_RESOURCE_KIND_SESSION) {
|
|
18591
|
+
return { ...value, kind: RESOURCE_KIND_SESSION };
|
|
18592
|
+
}
|
|
18593
|
+
return value;
|
|
18594
|
+
}
|
|
18595
|
+
function isRecord(value) {
|
|
18596
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
18597
|
+
}
|
|
18598
|
+
var EnvironmentApplyDocumentSchema, IdentityApplyDocumentSchema, SessionApplyDocumentSchema, ProjectApplyResourceSchema, PROJECT_RESOURCE_APPLY_ORDER, PROJECT_RESOURCE_KINDS, ProjectDeleteResourceBaseSchema, ProjectDeleteResourceSchema, AVATAR_ASSET_CONTENT_TYPES, MAX_AVATAR_ASSET_BASE64_LENGTH, ProjectApplyAssetSchema, ProjectApplyAssetsSchema, AvatarAssetUploadRequestSchema, AvatarAssetUploadResponseSchema, ProjectApplyRequestSchema, ProjectApplySystemConfigSchema, ProjectAppliedResourceSchema, ProjectApplyDiagnosticSchema, ProjectApplyResponseResourceKindSchema, ProjectApplyResponseSchema;
|
|
18588
18599
|
var init_project_resources = __esm({
|
|
18589
18600
|
"../../packages/schemas/src/project-resources.ts"() {
|
|
18590
18601
|
"use strict";
|
|
@@ -18605,21 +18616,29 @@ var init_project_resources = __esm({
|
|
|
18605
18616
|
RESOURCE_KIND_SESSION,
|
|
18606
18617
|
SessionApplyRequestSchema.shape.spec
|
|
18607
18618
|
);
|
|
18608
|
-
ProjectApplyResourceSchema = external_exports.
|
|
18609
|
-
|
|
18610
|
-
|
|
18611
|
-
|
|
18612
|
-
|
|
18619
|
+
ProjectApplyResourceSchema = external_exports.preprocess(
|
|
18620
|
+
normalizeLegacySessionKind,
|
|
18621
|
+
external_exports.discriminatedUnion("kind", [
|
|
18622
|
+
EnvironmentApplyDocumentSchema,
|
|
18623
|
+
IdentityApplyDocumentSchema,
|
|
18624
|
+
SessionApplyDocumentSchema
|
|
18625
|
+
])
|
|
18626
|
+
);
|
|
18613
18627
|
PROJECT_RESOURCE_APPLY_ORDER = [
|
|
18614
18628
|
RESOURCE_KIND_ENVIRONMENT,
|
|
18615
18629
|
RESOURCE_KIND_IDENTITY,
|
|
18616
18630
|
RESOURCE_KIND_SESSION
|
|
18617
18631
|
];
|
|
18618
18632
|
PROJECT_RESOURCE_KINDS = PROJECT_RESOURCE_APPLY_ORDER;
|
|
18619
|
-
|
|
18620
|
-
kind: external_exports.enum(PROJECT_RESOURCE_KINDS),
|
|
18633
|
+
ProjectDeleteResourceBaseSchema = external_exports.object({
|
|
18621
18634
|
name: external_exports.string().trim().min(1)
|
|
18622
18635
|
});
|
|
18636
|
+
ProjectDeleteResourceSchema = external_exports.preprocess(
|
|
18637
|
+
normalizeLegacySessionKind,
|
|
18638
|
+
ProjectDeleteResourceBaseSchema.extend({
|
|
18639
|
+
kind: external_exports.enum(PROJECT_RESOURCE_KINDS)
|
|
18640
|
+
})
|
|
18641
|
+
);
|
|
18623
18642
|
AVATAR_ASSET_CONTENT_TYPES = ["image/png", "image/jpeg"];
|
|
18624
18643
|
MAX_AVATAR_ASSET_BASE64_LENGTH = Math.ceil(MAX_AVATAR_ASSET_BYTES / 3) * 4 + 4;
|
|
18625
18644
|
ProjectApplyAssetSchema = external_exports.object({
|
|
@@ -18673,6 +18692,12 @@ var init_project_resources = __esm({
|
|
|
18673
18692
|
]),
|
|
18674
18693
|
name: external_exports.string().min(1)
|
|
18675
18694
|
});
|
|
18695
|
+
ProjectApplyResponseResourceKindSchema = external_exports.union([
|
|
18696
|
+
external_exports.enum(PROJECT_RESOURCE_KINDS),
|
|
18697
|
+
external_exports.literal(LEGACY_RESOURCE_KIND_SESSION)
|
|
18698
|
+
]).transform(
|
|
18699
|
+
(kind) => kind === LEGACY_RESOURCE_KIND_SESSION ? RESOURCE_KIND_SESSION : kind
|
|
18700
|
+
);
|
|
18676
18701
|
ProjectApplyResponseSchema = external_exports.object({
|
|
18677
18702
|
dryRun: external_exports.boolean().default(false),
|
|
18678
18703
|
resources: external_exports.array(ProjectAppliedResourceSchema),
|
|
@@ -18681,14 +18706,14 @@ var init_project_resources = __esm({
|
|
|
18681
18706
|
plan: external_exports.array(
|
|
18682
18707
|
external_exports.object({
|
|
18683
18708
|
action: external_exports.enum(["create", "update", "unchanged", "archive"]),
|
|
18684
|
-
kind:
|
|
18709
|
+
kind: ProjectApplyResponseResourceKindSchema,
|
|
18685
18710
|
name: external_exports.string().min(1),
|
|
18686
18711
|
uid: external_exports.string().min(1).optional()
|
|
18687
18712
|
})
|
|
18688
18713
|
).default([]),
|
|
18689
18714
|
pruned: external_exports.array(
|
|
18690
18715
|
external_exports.object({
|
|
18691
|
-
kind:
|
|
18716
|
+
kind: ProjectApplyResponseResourceKindSchema,
|
|
18692
18717
|
name: external_exports.string().min(1),
|
|
18693
18718
|
uid: external_exports.string().min(1)
|
|
18694
18719
|
})
|
|
@@ -21183,7 +21208,7 @@ var init_package = __esm({
|
|
|
21183
21208
|
"package.json"() {
|
|
21184
21209
|
package_default = {
|
|
21185
21210
|
name: "@autohq/cli",
|
|
21186
|
-
version: "0.1.
|
|
21211
|
+
version: "0.1.135",
|
|
21187
21212
|
license: "SEE LICENSE IN README.md",
|
|
21188
21213
|
publishConfig: {
|
|
21189
21214
|
access: "public"
|
|
@@ -21317,7 +21342,7 @@ function stampAvatarSha256(resource, assets) {
|
|
|
21317
21342
|
}
|
|
21318
21343
|
};
|
|
21319
21344
|
}
|
|
21320
|
-
if (resource.kind ===
|
|
21345
|
+
if (resource.kind === RESOURCE_KIND_SESSION && typeof resource.spec.identity === "object" && resource.spec.identity !== null && !Array.isArray(resource.spec.identity)) {
|
|
21321
21346
|
const identity2 = resource.spec.identity;
|
|
21322
21347
|
const avatar = identity2.avatar;
|
|
21323
21348
|
const asset = avatar ? assets[avatar.asset] : void 0;
|
|
@@ -21344,19 +21369,14 @@ var MAX_APPLY_REQUEST_BODY_BYTES;
|
|
|
21344
21369
|
var init_assets = __esm({
|
|
21345
21370
|
"src/commands/apply/assets.ts"() {
|
|
21346
21371
|
"use strict";
|
|
21372
|
+
init_src();
|
|
21347
21373
|
init_resources2();
|
|
21348
21374
|
MAX_APPLY_REQUEST_BODY_BYTES = 4 * 1024 * 1024;
|
|
21349
21375
|
}
|
|
21350
21376
|
});
|
|
21351
21377
|
|
|
21352
|
-
// src/commands/
|
|
21353
|
-
import {
|
|
21354
|
-
import {
|
|
21355
|
-
readFileSync as readFileSync3,
|
|
21356
|
-
readdirSync as readdirSync2,
|
|
21357
|
-
realpathSync,
|
|
21358
|
-
statSync
|
|
21359
|
-
} from "fs";
|
|
21378
|
+
// src/commands/agents/authoring.ts
|
|
21379
|
+
import { readFileSync as readFileSync3, readdirSync as readdirSync2, statSync } from "fs";
|
|
21360
21380
|
import {
|
|
21361
21381
|
basename as basename2,
|
|
21362
21382
|
dirname as dirname4,
|
|
@@ -21365,7 +21385,436 @@ import {
|
|
|
21365
21385
|
join as join3,
|
|
21366
21386
|
resolve
|
|
21367
21387
|
} from "path";
|
|
21368
|
-
import { parseAllDocuments as parseYamlDocuments } from "yaml";
|
|
21388
|
+
import { parseAllDocuments as parseYamlDocuments, stringify } from "yaml";
|
|
21389
|
+
function compileAgentFile(path2) {
|
|
21390
|
+
const context = { graph: /* @__PURE__ */ new Map(), removals: [] };
|
|
21391
|
+
const document = readSingleDocument(path2);
|
|
21392
|
+
const compiled = normalizeAgentDocument(
|
|
21393
|
+
compileAgentDocument(document, path2, [], context)
|
|
21394
|
+
);
|
|
21395
|
+
const parsed = ProjectApplyResourceSchema.safeParse(compiled);
|
|
21396
|
+
if (!parsed.success) {
|
|
21397
|
+
throw new Error(`Invalid compiled agent ${path2}: ${parsed.error.message}`);
|
|
21398
|
+
}
|
|
21399
|
+
if (parsed.data.kind !== RESOURCE_KIND_SESSION) {
|
|
21400
|
+
throw new Error(
|
|
21401
|
+
`Invalid compiled agent ${path2}: expected kind "agent", got "${parsed.data.kind}"`
|
|
21402
|
+
);
|
|
21403
|
+
}
|
|
21404
|
+
return {
|
|
21405
|
+
resource: parsed.data,
|
|
21406
|
+
graph: [...context.graph.values()],
|
|
21407
|
+
removals: context.removals
|
|
21408
|
+
};
|
|
21409
|
+
}
|
|
21410
|
+
function compileAgentDocumentValue(document, path2) {
|
|
21411
|
+
const context = { graph: /* @__PURE__ */ new Map(), removals: [] };
|
|
21412
|
+
const compiled = normalizeAgentDocument(
|
|
21413
|
+
compileAgentDocument(document, path2, [], context)
|
|
21414
|
+
);
|
|
21415
|
+
const parsed = ProjectApplyResourceSchema.safeParse(compiled);
|
|
21416
|
+
if (!parsed.success) {
|
|
21417
|
+
throw new Error(`Invalid compiled agent ${path2}: ${parsed.error.message}`);
|
|
21418
|
+
}
|
|
21419
|
+
if (parsed.data.kind !== RESOURCE_KIND_SESSION) {
|
|
21420
|
+
throw new Error(
|
|
21421
|
+
`Invalid compiled agent ${path2}: expected kind "agent", got "${parsed.data.kind}"`
|
|
21422
|
+
);
|
|
21423
|
+
}
|
|
21424
|
+
return {
|
|
21425
|
+
resource: parsed.data,
|
|
21426
|
+
graph: [...context.graph.values()],
|
|
21427
|
+
removals: context.removals
|
|
21428
|
+
};
|
|
21429
|
+
}
|
|
21430
|
+
function renderCompiledAgentYaml(result) {
|
|
21431
|
+
return stringify(result.resource).trimEnd();
|
|
21432
|
+
}
|
|
21433
|
+
function renderAgentExplain(result) {
|
|
21434
|
+
const lines = [
|
|
21435
|
+
`agent ${result.resource.metadata.name}`,
|
|
21436
|
+
`files ${result.graph.length}`
|
|
21437
|
+
];
|
|
21438
|
+
for (const node of result.graph) {
|
|
21439
|
+
lines.push(node.path);
|
|
21440
|
+
for (const imported of node.imports) {
|
|
21441
|
+
lines.push(` imports ${imported}`);
|
|
21442
|
+
}
|
|
21443
|
+
}
|
|
21444
|
+
if (result.removals.length > 0) {
|
|
21445
|
+
lines.push("removals");
|
|
21446
|
+
for (const removal of result.removals) {
|
|
21447
|
+
lines.push(
|
|
21448
|
+
` ${removal.target}: ${removal.names.join(", ")} (${removal.path})`
|
|
21449
|
+
);
|
|
21450
|
+
}
|
|
21451
|
+
}
|
|
21452
|
+
return lines.join("\n");
|
|
21453
|
+
}
|
|
21454
|
+
function readLocalAgentAuthoringStatuses(input) {
|
|
21455
|
+
const agentsDirectory = join3(
|
|
21456
|
+
input?.directory ?? join3(process.cwd(), ".auto"),
|
|
21457
|
+
"agents"
|
|
21458
|
+
);
|
|
21459
|
+
let entries;
|
|
21460
|
+
try {
|
|
21461
|
+
entries = readdirSync2(agentsDirectory).filter(
|
|
21462
|
+
(entry) => AGENT_FILE_EXTENSIONS.includes(
|
|
21463
|
+
extname(
|
|
21464
|
+
entry
|
|
21465
|
+
).toLowerCase()
|
|
21466
|
+
)
|
|
21467
|
+
).sort((left, right) => left.localeCompare(right));
|
|
21468
|
+
} catch {
|
|
21469
|
+
return [];
|
|
21470
|
+
}
|
|
21471
|
+
return entries.map((entry) => {
|
|
21472
|
+
const path2 = join3(agentsDirectory, entry);
|
|
21473
|
+
const fallbackName = basename2(entry, extname(entry));
|
|
21474
|
+
try {
|
|
21475
|
+
const result = compileAgentFile(path2);
|
|
21476
|
+
return {
|
|
21477
|
+
name: result.resource.metadata.name,
|
|
21478
|
+
path: path2,
|
|
21479
|
+
ok: true,
|
|
21480
|
+
imports: importedEdgeCount(result.graph),
|
|
21481
|
+
removals: result.removals.reduce(
|
|
21482
|
+
(count, removal) => count + removal.names.length,
|
|
21483
|
+
0
|
|
21484
|
+
)
|
|
21485
|
+
};
|
|
21486
|
+
} catch (error51) {
|
|
21487
|
+
return {
|
|
21488
|
+
name: fallbackName,
|
|
21489
|
+
path: path2,
|
|
21490
|
+
ok: false,
|
|
21491
|
+
imports: 0,
|
|
21492
|
+
removals: 0,
|
|
21493
|
+
error: error51 instanceof Error ? error51.message : String(error51)
|
|
21494
|
+
};
|
|
21495
|
+
}
|
|
21496
|
+
});
|
|
21497
|
+
}
|
|
21498
|
+
function importedAgentAuthoringPaths(paths) {
|
|
21499
|
+
const imported = /* @__PURE__ */ new Set();
|
|
21500
|
+
for (const path2 of paths) {
|
|
21501
|
+
for (const document of readDocuments(path2)) {
|
|
21502
|
+
discoverImports(document, path2, [], imported);
|
|
21503
|
+
}
|
|
21504
|
+
}
|
|
21505
|
+
return imported;
|
|
21506
|
+
}
|
|
21507
|
+
function resolveAgentAuthoringPath(input) {
|
|
21508
|
+
const candidate = resolve(input.agent);
|
|
21509
|
+
if (isAgentFile(candidate)) {
|
|
21510
|
+
return candidate;
|
|
21511
|
+
}
|
|
21512
|
+
const agentsDirectory = join3(
|
|
21513
|
+
input.directory ?? join3(process.cwd(), ".auto"),
|
|
21514
|
+
"agents"
|
|
21515
|
+
);
|
|
21516
|
+
for (const extension of AGENT_FILE_EXTENSIONS) {
|
|
21517
|
+
const path2 = join3(agentsDirectory, `${input.agent}${extension}`);
|
|
21518
|
+
if (isAgentFile(path2)) {
|
|
21519
|
+
return path2;
|
|
21520
|
+
}
|
|
21521
|
+
}
|
|
21522
|
+
throw new Error(
|
|
21523
|
+
`Agent authoring file not found for "${input.agent}" under ${agentsDirectory}`
|
|
21524
|
+
);
|
|
21525
|
+
}
|
|
21526
|
+
function discoverImports(document, path2, stack, imported) {
|
|
21527
|
+
const resolvedPath = resolve(path2);
|
|
21528
|
+
if (stack.includes(resolvedPath)) {
|
|
21529
|
+
throw new Error(
|
|
21530
|
+
`Agent import cycle detected: ${[...stack, resolvedPath].join(" -> ")}`
|
|
21531
|
+
);
|
|
21532
|
+
}
|
|
21533
|
+
if (!isRecord2(document)) {
|
|
21534
|
+
return;
|
|
21535
|
+
}
|
|
21536
|
+
for (const importPath of importPaths(document)) {
|
|
21537
|
+
const resolvedImport = resolveImportPath(importPath, resolvedPath);
|
|
21538
|
+
imported.add(resolvedImport);
|
|
21539
|
+
discoverImports(
|
|
21540
|
+
readSingleDocument(resolvedImport),
|
|
21541
|
+
resolvedImport,
|
|
21542
|
+
[...stack, resolvedPath],
|
|
21543
|
+
imported
|
|
21544
|
+
);
|
|
21545
|
+
}
|
|
21546
|
+
}
|
|
21547
|
+
function compileAgentDocument(document, path2, stack, context) {
|
|
21548
|
+
const resolvedPath = resolve(path2);
|
|
21549
|
+
if (stack.includes(resolvedPath)) {
|
|
21550
|
+
throw new Error(
|
|
21551
|
+
`Agent import cycle detected: ${[...stack, resolvedPath].join(" -> ")}`
|
|
21552
|
+
);
|
|
21553
|
+
}
|
|
21554
|
+
if (!isRecord2(document)) {
|
|
21555
|
+
throw new Error(`Invalid agent authoring file ${path2}: expected object`);
|
|
21556
|
+
}
|
|
21557
|
+
const imports = importPaths(document).map(
|
|
21558
|
+
(importPath) => resolveImportPath(importPath, resolvedPath)
|
|
21559
|
+
);
|
|
21560
|
+
context.graph.set(resolvedPath, { path: resolvedPath, imports });
|
|
21561
|
+
let merged = {};
|
|
21562
|
+
for (const imported of imports) {
|
|
21563
|
+
const importedDocument = readSingleDocument(imported);
|
|
21564
|
+
merged = mergeValues2(
|
|
21565
|
+
merged,
|
|
21566
|
+
compileAgentDocument(
|
|
21567
|
+
importedDocument,
|
|
21568
|
+
imported,
|
|
21569
|
+
[...stack, resolvedPath],
|
|
21570
|
+
context
|
|
21571
|
+
),
|
|
21572
|
+
[]
|
|
21573
|
+
);
|
|
21574
|
+
}
|
|
21575
|
+
const removals = removalDirectives(document, resolvedPath);
|
|
21576
|
+
for (const removal of removals) {
|
|
21577
|
+
merged = applyRemoval(merged, removal);
|
|
21578
|
+
context.removals.push(removal);
|
|
21579
|
+
}
|
|
21580
|
+
return mergeValues2(merged, sanitizedAgentDocument(document), []);
|
|
21581
|
+
}
|
|
21582
|
+
function readSingleDocument(path2) {
|
|
21583
|
+
const documents = readDocuments(path2);
|
|
21584
|
+
if (documents.length !== 1) {
|
|
21585
|
+
throw new Error(
|
|
21586
|
+
`Agent authoring file ${path2} must contain exactly one document`
|
|
21587
|
+
);
|
|
21588
|
+
}
|
|
21589
|
+
return documents[0];
|
|
21590
|
+
}
|
|
21591
|
+
function readDocuments(path2) {
|
|
21592
|
+
const source = readFileSync3(path2, "utf8");
|
|
21593
|
+
return parseYamlDocuments(source).filter((document) => document.contents !== null).map((document) => document.toJSON());
|
|
21594
|
+
}
|
|
21595
|
+
function importPaths(document) {
|
|
21596
|
+
const value = document.imports ?? document.import;
|
|
21597
|
+
if (value === void 0) {
|
|
21598
|
+
return [];
|
|
21599
|
+
}
|
|
21600
|
+
const values = Array.isArray(value) ? value : [value];
|
|
21601
|
+
return values.map((item) => {
|
|
21602
|
+
if (typeof item !== "string" || item.trim().length === 0) {
|
|
21603
|
+
throw new Error("Agent imports must be non-empty string paths");
|
|
21604
|
+
}
|
|
21605
|
+
return item.trim();
|
|
21606
|
+
});
|
|
21607
|
+
}
|
|
21608
|
+
function resolveImportPath(importPath, importerPath) {
|
|
21609
|
+
if (isAbsolute(importPath) || /^[A-Za-z]+:\/\//.test(importPath)) {
|
|
21610
|
+
throw new Error(`Agent import must be a relative path: ${importPath}`);
|
|
21611
|
+
}
|
|
21612
|
+
const resolved = resolve(dirname4(importerPath), importPath);
|
|
21613
|
+
if (!isAgentFile(resolved)) {
|
|
21614
|
+
throw new Error(
|
|
21615
|
+
`Agent import not found: ${importPath} from ${importerPath}`
|
|
21616
|
+
);
|
|
21617
|
+
}
|
|
21618
|
+
return resolved;
|
|
21619
|
+
}
|
|
21620
|
+
function sanitizedAgentDocument(document) {
|
|
21621
|
+
const next = { ...document };
|
|
21622
|
+
delete next.import;
|
|
21623
|
+
delete next.imports;
|
|
21624
|
+
delete next.remove;
|
|
21625
|
+
if (isRecord2(next.spec) && "remove" in next.spec) {
|
|
21626
|
+
const spec = { ...next.spec };
|
|
21627
|
+
delete spec.remove;
|
|
21628
|
+
next.spec = spec;
|
|
21629
|
+
}
|
|
21630
|
+
return next;
|
|
21631
|
+
}
|
|
21632
|
+
function normalizeAgentDocument(document) {
|
|
21633
|
+
if (!isRecord2(document) || "kind" in document) {
|
|
21634
|
+
return document;
|
|
21635
|
+
}
|
|
21636
|
+
return { ...document, kind: RESOURCE_KIND_SESSION };
|
|
21637
|
+
}
|
|
21638
|
+
function removalDirectives(document, path2) {
|
|
21639
|
+
const raw = mergeValues2(
|
|
21640
|
+
isRecord2(document.remove) ? document.remove : {},
|
|
21641
|
+
isRecord2(document.spec) && isRecord2(document.spec.remove) ? document.spec.remove : {},
|
|
21642
|
+
[]
|
|
21643
|
+
);
|
|
21644
|
+
if (!isRecord2(raw)) {
|
|
21645
|
+
return [];
|
|
21646
|
+
}
|
|
21647
|
+
const directives = [];
|
|
21648
|
+
for (const [target, value] of Object.entries(raw)) {
|
|
21649
|
+
const names = stringList(value, `remove.${target}`);
|
|
21650
|
+
if (names.length > 0) {
|
|
21651
|
+
directives.push({ path: path2, target, names });
|
|
21652
|
+
}
|
|
21653
|
+
}
|
|
21654
|
+
return directives;
|
|
21655
|
+
}
|
|
21656
|
+
function stringList(value, label) {
|
|
21657
|
+
const values = Array.isArray(value) ? value : [value];
|
|
21658
|
+
return values.map((item) => {
|
|
21659
|
+
if (typeof item !== "string" || item.trim().length === 0) {
|
|
21660
|
+
throw new Error(`${label} entries must be non-empty strings`);
|
|
21661
|
+
}
|
|
21662
|
+
return item.trim();
|
|
21663
|
+
});
|
|
21664
|
+
}
|
|
21665
|
+
function applyRemoval(value, removal) {
|
|
21666
|
+
if (!isRecord2(value)) {
|
|
21667
|
+
return value;
|
|
21668
|
+
}
|
|
21669
|
+
const next = structuredClone(value);
|
|
21670
|
+
if (!isRecord2(next.spec)) {
|
|
21671
|
+
return next;
|
|
21672
|
+
}
|
|
21673
|
+
const spec = { ...next.spec };
|
|
21674
|
+
next.spec = spec;
|
|
21675
|
+
switch (removal.target) {
|
|
21676
|
+
case "tools":
|
|
21677
|
+
case "env": {
|
|
21678
|
+
if (!isRecord2(spec[removal.target])) {
|
|
21679
|
+
return next;
|
|
21680
|
+
}
|
|
21681
|
+
const collection = {
|
|
21682
|
+
...spec[removal.target]
|
|
21683
|
+
};
|
|
21684
|
+
for (const name of removal.names) {
|
|
21685
|
+
delete collection[name];
|
|
21686
|
+
}
|
|
21687
|
+
spec[removal.target] = collection;
|
|
21688
|
+
return next;
|
|
21689
|
+
}
|
|
21690
|
+
case "mounts":
|
|
21691
|
+
case "triggers": {
|
|
21692
|
+
const value2 = spec[removal.target];
|
|
21693
|
+
if (!Array.isArray(value2)) {
|
|
21694
|
+
return next;
|
|
21695
|
+
}
|
|
21696
|
+
const collection = value2;
|
|
21697
|
+
spec[removal.target] = collection.filter(
|
|
21698
|
+
(item) => !removal.names.includes(
|
|
21699
|
+
collectionItemKey(removal.target, item) ?? ""
|
|
21700
|
+
)
|
|
21701
|
+
);
|
|
21702
|
+
return next;
|
|
21703
|
+
}
|
|
21704
|
+
default:
|
|
21705
|
+
throw new Error(
|
|
21706
|
+
`Unsupported agent remove target "${removal.target}"; supported targets are tools, env, mounts, triggers`
|
|
21707
|
+
);
|
|
21708
|
+
}
|
|
21709
|
+
}
|
|
21710
|
+
function mergeValues2(base, override, path2) {
|
|
21711
|
+
if (override === void 0) {
|
|
21712
|
+
return base;
|
|
21713
|
+
}
|
|
21714
|
+
if (Array.isArray(base) && Array.isArray(override)) {
|
|
21715
|
+
return mergeArrays(base, override, path2);
|
|
21716
|
+
}
|
|
21717
|
+
if (isRecord2(base) && isRecord2(override)) {
|
|
21718
|
+
const merged = { ...base };
|
|
21719
|
+
for (const [key, value] of Object.entries(override)) {
|
|
21720
|
+
merged[key] = mergeValues2(merged[key], value, [...path2, key]);
|
|
21721
|
+
}
|
|
21722
|
+
return merged;
|
|
21723
|
+
}
|
|
21724
|
+
return override;
|
|
21725
|
+
}
|
|
21726
|
+
function mergeArrays(base, override, path2) {
|
|
21727
|
+
const pathKey = path2.join(".");
|
|
21728
|
+
if (pathKey !== "spec.mounts" && pathKey !== "spec.triggers") {
|
|
21729
|
+
return [...base, ...override];
|
|
21730
|
+
}
|
|
21731
|
+
const merged = [...base];
|
|
21732
|
+
const indexByKey = /* @__PURE__ */ new Map();
|
|
21733
|
+
for (const [index, item] of merged.entries()) {
|
|
21734
|
+
const key = collectionItemKey(path2.at(-1) ?? "", item);
|
|
21735
|
+
if (key) {
|
|
21736
|
+
indexByKey.set(key, index);
|
|
21737
|
+
}
|
|
21738
|
+
}
|
|
21739
|
+
for (const item of override) {
|
|
21740
|
+
const key = collectionItemKey(path2.at(-1) ?? "", item);
|
|
21741
|
+
if (key && indexByKey.has(key)) {
|
|
21742
|
+
merged[indexByKey.get(key) ?? 0] = mergeValues2(
|
|
21743
|
+
merged[indexByKey.get(key) ?? 0],
|
|
21744
|
+
item,
|
|
21745
|
+
path2
|
|
21746
|
+
);
|
|
21747
|
+
continue;
|
|
21748
|
+
}
|
|
21749
|
+
if (key) {
|
|
21750
|
+
indexByKey.set(key, merged.length);
|
|
21751
|
+
}
|
|
21752
|
+
merged.push(item);
|
|
21753
|
+
}
|
|
21754
|
+
return merged;
|
|
21755
|
+
}
|
|
21756
|
+
function collectionItemKey(collection, item) {
|
|
21757
|
+
if (!isRecord2(item)) {
|
|
21758
|
+
return void 0;
|
|
21759
|
+
}
|
|
21760
|
+
if (typeof item.name === "string") {
|
|
21761
|
+
return item.name;
|
|
21762
|
+
}
|
|
21763
|
+
if (collection === "mounts" && typeof item.mountPath === "string") {
|
|
21764
|
+
return item.mountPath;
|
|
21765
|
+
}
|
|
21766
|
+
if (collection === "triggers") {
|
|
21767
|
+
if (typeof item.event === "string") {
|
|
21768
|
+
return item.event;
|
|
21769
|
+
}
|
|
21770
|
+
if (Array.isArray(item.events)) {
|
|
21771
|
+
return item.events.filter((event) => typeof event === "string").join(",");
|
|
21772
|
+
}
|
|
21773
|
+
if (typeof item.cron === "string") {
|
|
21774
|
+
return `cron:${item.cron}:${typeof item.timezone === "string" ? item.timezone : ""}`;
|
|
21775
|
+
}
|
|
21776
|
+
}
|
|
21777
|
+
return void 0;
|
|
21778
|
+
}
|
|
21779
|
+
function importedEdgeCount(graph) {
|
|
21780
|
+
return graph.reduce((count, node) => count + node.imports.length, 0);
|
|
21781
|
+
}
|
|
21782
|
+
function isAgentFile(path2) {
|
|
21783
|
+
try {
|
|
21784
|
+
return statSync(path2).isFile();
|
|
21785
|
+
} catch {
|
|
21786
|
+
return false;
|
|
21787
|
+
}
|
|
21788
|
+
}
|
|
21789
|
+
function isRecord2(value) {
|
|
21790
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
21791
|
+
}
|
|
21792
|
+
var AGENT_FILE_EXTENSIONS;
|
|
21793
|
+
var init_authoring = __esm({
|
|
21794
|
+
"src/commands/agents/authoring.ts"() {
|
|
21795
|
+
"use strict";
|
|
21796
|
+
init_src();
|
|
21797
|
+
AGENT_FILE_EXTENSIONS = [".yaml", ".yml", ".json"];
|
|
21798
|
+
}
|
|
21799
|
+
});
|
|
21800
|
+
|
|
21801
|
+
// src/commands/apply/files.ts
|
|
21802
|
+
import { createHash as createHash3 } from "crypto";
|
|
21803
|
+
import {
|
|
21804
|
+
readFileSync as readFileSync4,
|
|
21805
|
+
readdirSync as readdirSync3,
|
|
21806
|
+
realpathSync,
|
|
21807
|
+
statSync as statSync2
|
|
21808
|
+
} from "fs";
|
|
21809
|
+
import {
|
|
21810
|
+
basename as basename3,
|
|
21811
|
+
dirname as dirname5,
|
|
21812
|
+
extname as extname2,
|
|
21813
|
+
isAbsolute as isAbsolute2,
|
|
21814
|
+
join as join4,
|
|
21815
|
+
resolve as resolve2
|
|
21816
|
+
} from "path";
|
|
21817
|
+
import { parseAllDocuments as parseYamlDocuments2 } from "yaml";
|
|
21369
21818
|
function readProjectApplyRequest(options) {
|
|
21370
21819
|
if (options.file && options.directory) {
|
|
21371
21820
|
throw new Error("Cannot use --file with --directory.");
|
|
@@ -21378,7 +21827,7 @@ function readProjectApplyRequest(options) {
|
|
|
21378
21827
|
);
|
|
21379
21828
|
return { ...request, assets: assets2 };
|
|
21380
21829
|
}
|
|
21381
|
-
const directory = options.directory ??
|
|
21830
|
+
const directory = options.directory ?? join4(process.cwd(), ".auto");
|
|
21382
21831
|
const files = applyFiles(directory);
|
|
21383
21832
|
if (files.length === 0) {
|
|
21384
21833
|
throw new Error(`No resource files found in ${directory}`);
|
|
@@ -21389,7 +21838,7 @@ function readProjectApplyRequest(options) {
|
|
|
21389
21838
|
for (const resource of request.resources) {
|
|
21390
21839
|
if (resource.kind !== kind) {
|
|
21391
21840
|
throw new Error(
|
|
21392
|
-
`Resource kind "${resource.kind}" in ${path2} does not match .auto/${
|
|
21841
|
+
`Resource kind "${resource.kind}" in ${path2} does not match .auto/${primaryApplyDirectory(kind)}`
|
|
21393
21842
|
);
|
|
21394
21843
|
}
|
|
21395
21844
|
resources.push(resource);
|
|
@@ -21430,23 +21879,30 @@ function mcpOAuthSessionToolConnectionsFromAppliedResources(resources) {
|
|
|
21430
21879
|
function applyFiles(root) {
|
|
21431
21880
|
const files = [];
|
|
21432
21881
|
for (const kind of PROJECT_RESOURCE_APPLY_ORDER) {
|
|
21433
|
-
const
|
|
21434
|
-
const
|
|
21435
|
-
|
|
21436
|
-
|
|
21437
|
-
|
|
21438
|
-
|
|
21439
|
-
|
|
21882
|
+
const kindFiles = [];
|
|
21883
|
+
for (const directory of applyDirectories(kind)) {
|
|
21884
|
+
const path2 = join4(root, directory);
|
|
21885
|
+
let entries;
|
|
21886
|
+
try {
|
|
21887
|
+
entries = readdirSync3(path2, { withFileTypes: true });
|
|
21888
|
+
} catch {
|
|
21889
|
+
continue;
|
|
21890
|
+
}
|
|
21891
|
+
kindFiles.push(...resourceApplyFiles(path2, entries));
|
|
21440
21892
|
}
|
|
21441
|
-
|
|
21442
|
-
|
|
21443
|
-
|
|
21444
|
-
|
|
21445
|
-
|
|
21446
|
-
);
|
|
21893
|
+
const importedFiles = kind === RESOURCE_KIND_SESSION ? importedAgentAuthoringPaths(kindFiles) : /* @__PURE__ */ new Set();
|
|
21894
|
+
const appliedFiles = kind === RESOURCE_KIND_SESSION ? kindFiles.filter(
|
|
21895
|
+
(path2) => !importedFiles.has(resolve2(path2)) && !isSharedAgentAuthoringFile(root, path2)
|
|
21896
|
+
) : kindFiles;
|
|
21897
|
+
files.push(...appliedFiles.map((path2) => ({ kind, path: path2 })));
|
|
21447
21898
|
}
|
|
21448
21899
|
return files;
|
|
21449
21900
|
}
|
|
21901
|
+
function isSharedAgentAuthoringFile(root, path2) {
|
|
21902
|
+
const agentsSharedRoot = resolve2(root, "agents", "shared");
|
|
21903
|
+
const resolvedPath = resolve2(path2);
|
|
21904
|
+
return resolvedPath.startsWith(`${agentsSharedRoot}/`);
|
|
21905
|
+
}
|
|
21450
21906
|
function mcpOAuthSessionToolConnectionsFromSessionTools(input) {
|
|
21451
21907
|
return Object.entries(input.tools).flatMap(([alias, tool]) => {
|
|
21452
21908
|
if (tool.kind !== "mcp_remote" || tool.disabled || tool.auth.kind !== "mcp_oauth") {
|
|
@@ -21462,10 +21918,10 @@ function mcpOAuthSessionToolConnectionsFromSessionTools(input) {
|
|
|
21462
21918
|
});
|
|
21463
21919
|
}
|
|
21464
21920
|
function readApplyDocumentFile(path2) {
|
|
21465
|
-
const source =
|
|
21921
|
+
const source = readFileSync4(path2, "utf8");
|
|
21466
21922
|
let documents;
|
|
21467
21923
|
try {
|
|
21468
|
-
documents =
|
|
21924
|
+
documents = parseYamlDocuments2(source).filter((document) => document.contents !== null).map((document) => document.toJSON());
|
|
21469
21925
|
} catch (error51) {
|
|
21470
21926
|
throw new Error(
|
|
21471
21927
|
`Invalid apply file: ${error51 instanceof Error ? error51.message : String(error51)}`
|
|
@@ -21484,12 +21940,15 @@ function readApplyDocumentFile(path2) {
|
|
|
21484
21940
|
delete: [],
|
|
21485
21941
|
dryRun: false,
|
|
21486
21942
|
prune: false,
|
|
21487
|
-
resources: documents.map(readApplyDocument),
|
|
21943
|
+
resources: documents.map((document) => readApplyDocument(document, path2)),
|
|
21488
21944
|
assets: {}
|
|
21489
21945
|
};
|
|
21490
21946
|
}
|
|
21491
|
-
function readApplyDocument(document) {
|
|
21947
|
+
function readApplyDocument(document, path2) {
|
|
21492
21948
|
const candidate = applyCandidate(document);
|
|
21949
|
+
if (candidate.kind === RESOURCE_KIND_SESSION) {
|
|
21950
|
+
return compileAgentDocumentValue(document, path2).resource;
|
|
21951
|
+
}
|
|
21493
21952
|
const parsed = APPLY_SCHEMAS[candidate.kind].safeParse(candidate.value);
|
|
21494
21953
|
if (!parsed.success) {
|
|
21495
21954
|
throw new Error(
|
|
@@ -21514,7 +21973,7 @@ function readApplyAssets(resources, projectRoot) {
|
|
|
21514
21973
|
resourceName: target.resourceName,
|
|
21515
21974
|
projectRoot
|
|
21516
21975
|
});
|
|
21517
|
-
const bytes =
|
|
21976
|
+
const bytes = readFileSync4(resolvedPath);
|
|
21518
21977
|
const dimensionsProblem = avatarAssetDimensionsProblem(bytes);
|
|
21519
21978
|
if (dimensionsProblem) {
|
|
21520
21979
|
throw new Error(
|
|
@@ -21523,7 +21982,7 @@ function readApplyAssets(resources, projectRoot) {
|
|
|
21523
21982
|
}
|
|
21524
21983
|
assets[target.asset] = {
|
|
21525
21984
|
sha256: createHash3("sha256").update(bytes).digest("hex"),
|
|
21526
|
-
contentType:
|
|
21985
|
+
contentType: extname2(resolvedPath).toLowerCase() === ".png" ? "image/png" : "image/jpeg",
|
|
21527
21986
|
dataBase64: bytes.toString("base64")
|
|
21528
21987
|
};
|
|
21529
21988
|
}
|
|
@@ -21541,7 +22000,7 @@ function avatarAssetTarget(resource) {
|
|
|
21541
22000
|
return void 0;
|
|
21542
22001
|
}
|
|
21543
22002
|
function validateSessionAvatarAsset(input) {
|
|
21544
|
-
if (
|
|
22003
|
+
if (isAbsolute2(input.asset)) {
|
|
21545
22004
|
throw new Error(
|
|
21546
22005
|
`Invalid identity avatar asset for "${input.resourceName}": asset path must be relative`
|
|
21547
22006
|
);
|
|
@@ -21553,10 +22012,10 @@ function validateSessionAvatarAsset(input) {
|
|
|
21553
22012
|
);
|
|
21554
22013
|
}
|
|
21555
22014
|
const projectRoot = realpathSync(input.projectRoot);
|
|
21556
|
-
const assetPath =
|
|
22015
|
+
const assetPath = resolve2(projectRoot, input.asset);
|
|
21557
22016
|
let assetsRoot;
|
|
21558
22017
|
try {
|
|
21559
|
-
assetsRoot = realpathSync(
|
|
22018
|
+
assetsRoot = realpathSync(resolve2(projectRoot, ".auto", "assets"));
|
|
21560
22019
|
} catch {
|
|
21561
22020
|
throw new Error(
|
|
21562
22021
|
`Invalid identity avatar asset for "${input.resourceName}": .auto/assets directory does not exist`
|
|
@@ -21566,7 +22025,7 @@ function validateSessionAvatarAsset(input) {
|
|
|
21566
22025
|
let stat;
|
|
21567
22026
|
try {
|
|
21568
22027
|
resolvedAssetPath = realpathSync(assetPath);
|
|
21569
|
-
stat =
|
|
22028
|
+
stat = statSync2(resolvedAssetPath);
|
|
21570
22029
|
} catch {
|
|
21571
22030
|
throw new Error(
|
|
21572
22031
|
`Invalid identity avatar asset for "${input.resourceName}": ${input.asset} does not exist`
|
|
@@ -21587,7 +22046,7 @@ function validateSessionAvatarAsset(input) {
|
|
|
21587
22046
|
`Invalid identity avatar asset for "${input.resourceName}": asset must be ${MAX_AVATAR_ASSET_BYTES} bytes or smaller`
|
|
21588
22047
|
);
|
|
21589
22048
|
}
|
|
21590
|
-
const extension =
|
|
22049
|
+
const extension = extname2(resolvedAssetPath).toLowerCase();
|
|
21591
22050
|
if (!ALLOWED_AVATAR_EXTENSIONS.has(extension)) {
|
|
21592
22051
|
throw new Error(
|
|
21593
22052
|
`Invalid identity avatar asset for "${input.resourceName}": asset must be a PNG or JPEG image`
|
|
@@ -21596,16 +22055,16 @@ function validateSessionAvatarAsset(input) {
|
|
|
21596
22055
|
return resolvedAssetPath;
|
|
21597
22056
|
}
|
|
21598
22057
|
function applyProjectRoot(directory) {
|
|
21599
|
-
const resolved =
|
|
21600
|
-
return
|
|
22058
|
+
const resolved = resolve2(directory);
|
|
22059
|
+
return basename3(resolved) === ".auto" ? dirname5(resolved) : resolved;
|
|
21601
22060
|
}
|
|
21602
22061
|
function applyFileProjectRoot(file2) {
|
|
21603
|
-
let dir =
|
|
22062
|
+
let dir = dirname5(resolve2(file2));
|
|
21604
22063
|
while (true) {
|
|
21605
|
-
if (
|
|
21606
|
-
return
|
|
22064
|
+
if (basename3(dir) === ".auto") {
|
|
22065
|
+
return dirname5(dir);
|
|
21607
22066
|
}
|
|
21608
|
-
const parent =
|
|
22067
|
+
const parent = dirname5(dir);
|
|
21609
22068
|
if (parent === dir) {
|
|
21610
22069
|
return process.cwd();
|
|
21611
22070
|
}
|
|
@@ -21616,12 +22075,21 @@ function isInside(path2, parent) {
|
|
|
21616
22075
|
return path2.startsWith(`${parent}/`);
|
|
21617
22076
|
}
|
|
21618
22077
|
function applyCandidate(document) {
|
|
21619
|
-
if (!
|
|
22078
|
+
if (!isRecord3(document) || !("kind" in document)) {
|
|
21620
22079
|
return { kind: RESOURCE_KIND_SESSION, value: document };
|
|
21621
22080
|
}
|
|
22081
|
+
if (document.kind === LEGACY_RESOURCE_KIND_SESSION) {
|
|
22082
|
+
return {
|
|
22083
|
+
kind: RESOURCE_KIND_SESSION,
|
|
22084
|
+
value: {
|
|
22085
|
+
metadata: document.metadata,
|
|
22086
|
+
spec: document.spec
|
|
22087
|
+
}
|
|
22088
|
+
};
|
|
22089
|
+
}
|
|
21622
22090
|
if (!PROJECT_RESOURCE_APPLY_ORDER.includes(document.kind)) {
|
|
21623
22091
|
throw new Error(
|
|
21624
|
-
`Unsupported apply resource kind "${String(document.kind)}"; supported kinds are ${PROJECT_RESOURCE_APPLY_ORDER.map((kind2) => `"${kind2}"`).join(", ")}`
|
|
22092
|
+
`Unsupported apply resource kind "${String(document.kind)}"; supported kinds are ${PROJECT_RESOURCE_APPLY_ORDER.map((kind2) => `"${kind2}"`).join(", ")} (legacy alias "session" is also accepted)`
|
|
21625
22093
|
);
|
|
21626
22094
|
}
|
|
21627
22095
|
const kind = document.kind;
|
|
@@ -21633,16 +22101,22 @@ function applyCandidate(document) {
|
|
|
21633
22101
|
}
|
|
21634
22102
|
};
|
|
21635
22103
|
}
|
|
21636
|
-
function
|
|
22104
|
+
function applyDirectories(kind) {
|
|
22105
|
+
return [...LEGACY_APPLY_DIRECTORIES[kind] ?? [], APPLY_DIRECTORIES[kind]];
|
|
22106
|
+
}
|
|
22107
|
+
function primaryApplyDirectory(kind) {
|
|
22108
|
+
return APPLY_DIRECTORIES[kind];
|
|
22109
|
+
}
|
|
22110
|
+
function isRecord3(value) {
|
|
21637
22111
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
21638
22112
|
}
|
|
21639
22113
|
function resourceApplyFiles(directory, entries) {
|
|
21640
22114
|
const files = [];
|
|
21641
22115
|
for (const entry of entries) {
|
|
21642
|
-
const path2 =
|
|
22116
|
+
const path2 = join4(directory, entry.name);
|
|
21643
22117
|
if (entry.isDirectory()) {
|
|
21644
22118
|
files.push(
|
|
21645
|
-
...resourceApplyFiles(path2,
|
|
22119
|
+
...resourceApplyFiles(path2, readdirSync3(path2, { withFileTypes: true }))
|
|
21646
22120
|
);
|
|
21647
22121
|
continue;
|
|
21648
22122
|
}
|
|
@@ -21652,20 +22126,24 @@ function resourceApplyFiles(directory, entries) {
|
|
|
21652
22126
|
}
|
|
21653
22127
|
return files.sort((left, right) => left.localeCompare(right));
|
|
21654
22128
|
}
|
|
21655
|
-
var APPLY_DIRECTORIES, APPLY_SCHEMAS, ALLOWED_AVATAR_EXTENSIONS;
|
|
22129
|
+
var APPLY_DIRECTORIES, LEGACY_APPLY_DIRECTORIES, APPLY_SCHEMAS, ALLOWED_AVATAR_EXTENSIONS;
|
|
21656
22130
|
var init_files = __esm({
|
|
21657
22131
|
"src/commands/apply/files.ts"() {
|
|
21658
22132
|
"use strict";
|
|
21659
22133
|
init_src();
|
|
22134
|
+
init_authoring();
|
|
21660
22135
|
APPLY_DIRECTORIES = {
|
|
21661
22136
|
environment: "environments",
|
|
21662
22137
|
identity: "identities",
|
|
21663
|
-
|
|
22138
|
+
agent: "agents"
|
|
22139
|
+
};
|
|
22140
|
+
LEGACY_APPLY_DIRECTORIES = {
|
|
22141
|
+
[RESOURCE_KIND_SESSION]: ["sessions"]
|
|
21664
22142
|
};
|
|
21665
22143
|
APPLY_SCHEMAS = {
|
|
21666
22144
|
environment: EnvironmentApplyRequestSchema,
|
|
21667
22145
|
identity: IdentityApplyRequestSchema,
|
|
21668
|
-
|
|
22146
|
+
[RESOURCE_KIND_SESSION]: SessionApplyRequestSchema
|
|
21669
22147
|
};
|
|
21670
22148
|
ALLOWED_AVATAR_EXTENSIONS = /* @__PURE__ */ new Set([".jpg", ".jpeg", ".png"]);
|
|
21671
22149
|
}
|
|
@@ -21754,7 +22232,7 @@ async function connectSessionTool(input) {
|
|
|
21754
22232
|
}
|
|
21755
22233
|
);
|
|
21756
22234
|
input.writeOutput(
|
|
21757
|
-
`connected
|
|
22235
|
+
`connected agent/${completed.session ?? input.session} tool/${completed.tool} connection/${completed.connection}`
|
|
21758
22236
|
);
|
|
21759
22237
|
} finally {
|
|
21760
22238
|
callback.close();
|
|
@@ -21792,7 +22270,7 @@ function sessionToolConnectionScopeContext(input) {
|
|
|
21792
22270
|
function writeConnectionStart(writeOutput, result) {
|
|
21793
22271
|
writeOutput(result.message);
|
|
21794
22272
|
writeOutput(
|
|
21795
|
-
`connect
|
|
22273
|
+
`connect agent/${result.session} tool/${result.tool} connection/${result.connection}`
|
|
21796
22274
|
);
|
|
21797
22275
|
writeOutput(`authorization_url ${result.authorizationUrl}`);
|
|
21798
22276
|
}
|
|
@@ -21871,7 +22349,7 @@ async function applyProjectInput(input) {
|
|
|
21871
22349
|
)) {
|
|
21872
22350
|
input.writeOutput(
|
|
21873
22351
|
style.dim(
|
|
21874
|
-
`would connect
|
|
22352
|
+
`would connect agent/${item.session} tool/${item.tool} connection/${item.connection}`
|
|
21875
22353
|
)
|
|
21876
22354
|
);
|
|
21877
22355
|
}
|
|
@@ -21959,8 +22437,11 @@ function parseProjectResourceReference(resource) {
|
|
|
21959
22437
|
throw new Error('Resource must be formatted as "kind/name"');
|
|
21960
22438
|
}
|
|
21961
22439
|
if (!PROJECT_RESOURCE_KINDS.includes(kind)) {
|
|
22440
|
+
if (kind === LEGACY_RESOURCE_KIND_SESSION) {
|
|
22441
|
+
return { kind: RESOURCE_KIND_SESSION, name };
|
|
22442
|
+
}
|
|
21962
22443
|
throw new Error(
|
|
21963
|
-
`Unsupported resource kind "${kind}"; supported kinds are ${PROJECT_RESOURCE_KINDS.map((value) => `"${value}"`).join(", ")}`
|
|
22444
|
+
`Unsupported resource kind "${kind}"; supported kinds are ${PROJECT_RESOURCE_KINDS.map((value) => `"${value}"`).join(", ")} (legacy alias "session" is also accepted)`
|
|
21964
22445
|
);
|
|
21965
22446
|
}
|
|
21966
22447
|
return { kind, name };
|
|
@@ -22006,8 +22487,8 @@ var init_resources3 = __esm({
|
|
|
22006
22487
|
[RESOURCE_KIND_SESSION]: {
|
|
22007
22488
|
kind: RESOURCE_KIND_SESSION,
|
|
22008
22489
|
section: "sessions",
|
|
22009
|
-
label: "
|
|
22010
|
-
pluralLabel: "
|
|
22490
|
+
label: "Agent",
|
|
22491
|
+
pluralLabel: "Agents"
|
|
22011
22492
|
}
|
|
22012
22493
|
};
|
|
22013
22494
|
PROJECT_RESOURCE_TUI_ORDER = [
|
|
@@ -22035,10 +22516,10 @@ var init_resources3 = __esm({
|
|
|
22035
22516
|
|
|
22036
22517
|
// src/commands/edit/actions.ts
|
|
22037
22518
|
import { spawn as spawn2 } from "child_process";
|
|
22038
|
-
import { mkdtempSync, readFileSync as
|
|
22519
|
+
import { mkdtempSync, readFileSync as readFileSync5, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
22039
22520
|
import { tmpdir } from "os";
|
|
22040
|
-
import { join as
|
|
22041
|
-
import { parseAllDocuments as
|
|
22521
|
+
import { join as join5 } from "path";
|
|
22522
|
+
import { parseAllDocuments as parseYamlDocuments3, stringify as stringify3 } from "yaml";
|
|
22042
22523
|
async function editResource(input) {
|
|
22043
22524
|
const reference = parseProjectResourceReference(input.resource);
|
|
22044
22525
|
const editor = resolveEditor({
|
|
@@ -22050,15 +22531,15 @@ async function editResource(input) {
|
|
|
22050
22531
|
apiBaseUrl: input.commandOptions.apiBaseUrl
|
|
22051
22532
|
});
|
|
22052
22533
|
const document = editableResourceDocument(reference.kind, current);
|
|
22053
|
-
const source = `${
|
|
22534
|
+
const source = `${stringify3(document).trimEnd()}
|
|
22054
22535
|
`;
|
|
22055
|
-
const tempRoot = mkdtempSync(
|
|
22056
|
-
const filePath =
|
|
22536
|
+
const tempRoot = mkdtempSync(join5(tmpdir(), "auto-edit-"));
|
|
22537
|
+
const filePath = join5(tempRoot, `${reference.kind}-${reference.name}.yaml`);
|
|
22057
22538
|
writeFileSync3(filePath, source, "utf8");
|
|
22058
22539
|
let removeTempFile = false;
|
|
22059
22540
|
try {
|
|
22060
22541
|
await runEditor(editor, filePath);
|
|
22061
|
-
const editedSource =
|
|
22542
|
+
const editedSource = readFileSync5(filePath, "utf8");
|
|
22062
22543
|
if (editedSource === source) {
|
|
22063
22544
|
input.writeOutput("No changes; skipped apply.");
|
|
22064
22545
|
removeTempFile = true;
|
|
@@ -22105,7 +22586,7 @@ function resolveEditor(input) {
|
|
|
22105
22586
|
return input.env.VISUAL?.trim() || input.env.EDITOR?.trim() || "vi";
|
|
22106
22587
|
}
|
|
22107
22588
|
async function runEditor(editor, filePath) {
|
|
22108
|
-
await new Promise((
|
|
22589
|
+
await new Promise((resolve3, reject) => {
|
|
22109
22590
|
const child = spawn2(editor, [filePath], {
|
|
22110
22591
|
shell: true,
|
|
22111
22592
|
stdio: "inherit"
|
|
@@ -22117,7 +22598,7 @@ async function runEditor(editor, filePath) {
|
|
|
22117
22598
|
});
|
|
22118
22599
|
child.on("close", (code, signal) => {
|
|
22119
22600
|
if (code === 0) {
|
|
22120
|
-
|
|
22601
|
+
resolve3();
|
|
22121
22602
|
return;
|
|
22122
22603
|
}
|
|
22123
22604
|
if (signal) {
|
|
@@ -22129,10 +22610,10 @@ async function runEditor(editor, filePath) {
|
|
|
22129
22610
|
});
|
|
22130
22611
|
}
|
|
22131
22612
|
function readEditedResource(filePath, expected) {
|
|
22132
|
-
const source =
|
|
22613
|
+
const source = readFileSync5(filePath, "utf8");
|
|
22133
22614
|
let documents;
|
|
22134
22615
|
try {
|
|
22135
|
-
documents =
|
|
22616
|
+
documents = parseYamlDocuments3(source).filter((document) => document.contents !== null).map((document) => document.toJSON());
|
|
22136
22617
|
} catch (error51) {
|
|
22137
22618
|
throw new Error(
|
|
22138
22619
|
`Invalid edited resource: ${error51 instanceof Error ? error51.message : String(error51)}`
|
|
@@ -23735,7 +24216,7 @@ function ConversationView({
|
|
|
23735
24216
|
{
|
|
23736
24217
|
name: "/back",
|
|
23737
24218
|
aliases: ["/exit"],
|
|
23738
|
-
description: "Go back to the
|
|
24219
|
+
description: "Go back to the agents dashboard",
|
|
23739
24220
|
shortcut: "esc",
|
|
23740
24221
|
action: () => {
|
|
23741
24222
|
setInput("");
|
|
@@ -23775,7 +24256,7 @@ function ConversationView({
|
|
|
23775
24256
|
/* @__PURE__ */ jsx4(Text4, { bold: true, children: "Welcome to Auto!" }),
|
|
23776
24257
|
/* @__PURE__ */ jsx4(Text4, { children: " " }),
|
|
23777
24258
|
/* @__PURE__ */ jsxs4(Box4, { gap: 2, children: [
|
|
23778
|
-
/* @__PURE__ */ jsx4(Text4, { color: "yellow", children: "
|
|
24259
|
+
/* @__PURE__ */ jsx4(Text4, { color: "yellow", children: "Agent " }),
|
|
23779
24260
|
/* @__PURE__ */ jsx4(Text4, { children: sessionName })
|
|
23780
24261
|
] }),
|
|
23781
24262
|
/* @__PURE__ */ jsxs4(Box4, { gap: 2, children: [
|
|
@@ -24058,13 +24539,13 @@ function sleep2(ms, signal) {
|
|
|
24058
24539
|
if (signal.aborted) {
|
|
24059
24540
|
return Promise.resolve();
|
|
24060
24541
|
}
|
|
24061
|
-
return new Promise((
|
|
24062
|
-
const timeout = setTimeout(
|
|
24542
|
+
return new Promise((resolve3) => {
|
|
24543
|
+
const timeout = setTimeout(resolve3, ms);
|
|
24063
24544
|
signal.addEventListener(
|
|
24064
24545
|
"abort",
|
|
24065
24546
|
() => {
|
|
24066
24547
|
clearTimeout(timeout);
|
|
24067
|
-
|
|
24548
|
+
resolve3();
|
|
24068
24549
|
},
|
|
24069
24550
|
{ once: true }
|
|
24070
24551
|
);
|
|
@@ -25252,20 +25733,25 @@ var init_RunsView = __esm({
|
|
|
25252
25733
|
// src/tui/SessionsView.tsx
|
|
25253
25734
|
import { Box as Box11, Text as Text12, useStdout as useStdout6 } from "ink";
|
|
25254
25735
|
import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
25255
|
-
function SessionsView({
|
|
25736
|
+
function SessionsView({
|
|
25737
|
+
sessions,
|
|
25738
|
+
selectedIndex,
|
|
25739
|
+
error: error51,
|
|
25740
|
+
authoringStatuses
|
|
25741
|
+
}) {
|
|
25256
25742
|
const { stdout } = useStdout6();
|
|
25257
25743
|
const termWidth = stdout?.columns ?? 120;
|
|
25258
25744
|
const numCols = 4;
|
|
25259
25745
|
const gapTotal = (numCols - 1) * TABLE_GAP;
|
|
25260
25746
|
const descriptionWidth = Math.max(
|
|
25261
25747
|
24,
|
|
25262
|
-
termWidth - TABLE_PADDING_X * 2 - COL_NAME2 -
|
|
25748
|
+
termWidth - TABLE_PADDING_X * 2 - COL_NAME2 - COL_AUTHORING - COL_UPDATED2 - gapTotal
|
|
25263
25749
|
);
|
|
25264
25750
|
return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", flexGrow: 1, children: [
|
|
25265
25751
|
/* @__PURE__ */ jsxs10(Box11, { paddingX: TABLE_PADDING_X, paddingBottom: 1, gap: TABLE_GAP, children: [
|
|
25266
25752
|
/* @__PURE__ */ jsx12(HeaderCell, { width: COL_NAME2, children: "NAME\u2191" }),
|
|
25267
|
-
/* @__PURE__ */ jsx12(HeaderCell, { width: descriptionWidth, children: "
|
|
25268
|
-
/* @__PURE__ */ jsx12(HeaderCell, { width:
|
|
25753
|
+
/* @__PURE__ */ jsx12(HeaderCell, { width: descriptionWidth, children: "CONFIG" }),
|
|
25754
|
+
/* @__PURE__ */ jsx12(HeaderCell, { width: COL_AUTHORING, justifyContent: "flex-end", children: "AUTHORING" }),
|
|
25269
25755
|
/* @__PURE__ */ jsx12(HeaderCell, { width: COL_UPDATED2, justifyContent: "flex-end", children: "UPDATED" })
|
|
25270
25756
|
] }),
|
|
25271
25757
|
error51 ? /* @__PURE__ */ jsx12(Box11, { paddingX: 2, children: /* @__PURE__ */ jsxs10(Text12, { color: "red", children: [
|
|
@@ -25277,7 +25763,9 @@ function SessionsView({ sessions, selectedIndex, error: error51 }) {
|
|
|
25277
25763
|
] }) }) : sessions.map((session, i) => {
|
|
25278
25764
|
const isSelected = i === selectedIndex;
|
|
25279
25765
|
const updated = relativeTime3(session.metadata.updatedAt);
|
|
25280
|
-
const
|
|
25766
|
+
const authoring = sessionAuthoringLabel(
|
|
25767
|
+
authoringStatuses?.get(session.metadata.name)
|
|
25768
|
+
);
|
|
25281
25769
|
return /* @__PURE__ */ jsxs10(TableRow, { selected: isSelected, children: [
|
|
25282
25770
|
/* @__PURE__ */ jsx12(
|
|
25283
25771
|
TableCell,
|
|
@@ -25296,17 +25784,17 @@ function SessionsView({ sessions, selectedIndex, error: error51 }) {
|
|
|
25296
25784
|
width: descriptionWidth,
|
|
25297
25785
|
selected: isSelected,
|
|
25298
25786
|
dim: !isSelected,
|
|
25299
|
-
children:
|
|
25787
|
+
children: sessionConfigLabel(session)
|
|
25300
25788
|
}
|
|
25301
25789
|
),
|
|
25302
25790
|
/* @__PURE__ */ jsx12(
|
|
25303
25791
|
TableCell,
|
|
25304
25792
|
{
|
|
25305
|
-
width:
|
|
25793
|
+
width: COL_AUTHORING,
|
|
25306
25794
|
selected: isSelected,
|
|
25307
25795
|
dim: !isSelected,
|
|
25308
25796
|
justifyContent: "flex-end",
|
|
25309
|
-
children:
|
|
25797
|
+
children: authoring
|
|
25310
25798
|
}
|
|
25311
25799
|
),
|
|
25312
25800
|
/* @__PURE__ */ jsx12(
|
|
@@ -25323,6 +25811,26 @@ function SessionsView({ sessions, selectedIndex, error: error51 }) {
|
|
|
25323
25811
|
})
|
|
25324
25812
|
] });
|
|
25325
25813
|
}
|
|
25814
|
+
function sessionConfigLabel(session) {
|
|
25815
|
+
const pieces = [
|
|
25816
|
+
session.spec.harness,
|
|
25817
|
+
session.spec.environment ? `env:${session.spec.environment}` : void 0,
|
|
25818
|
+
`${session.status.runCount} runs`
|
|
25819
|
+
].filter(Boolean);
|
|
25820
|
+
return pieces.join(" ");
|
|
25821
|
+
}
|
|
25822
|
+
function sessionAuthoringLabel(status) {
|
|
25823
|
+
if (!status) {
|
|
25824
|
+
return "remote";
|
|
25825
|
+
}
|
|
25826
|
+
if (!status.ok) {
|
|
25827
|
+
return "error";
|
|
25828
|
+
}
|
|
25829
|
+
if (status.imports === 0 && status.removals === 0) {
|
|
25830
|
+
return "local ok";
|
|
25831
|
+
}
|
|
25832
|
+
return `ok i${status.imports}/r${status.removals}`;
|
|
25833
|
+
}
|
|
25326
25834
|
function relativeTime3(dateStr) {
|
|
25327
25835
|
const ms = Date.now() - new Date(dateStr).getTime();
|
|
25328
25836
|
const s = Math.floor(ms / 1e3);
|
|
@@ -25334,12 +25842,12 @@ function relativeTime3(dateStr) {
|
|
|
25334
25842
|
const d = Math.floor(h / 24);
|
|
25335
25843
|
return `${d}d ago`;
|
|
25336
25844
|
}
|
|
25337
|
-
var
|
|
25845
|
+
var COL_AUTHORING, COL_UPDATED2, COL_NAME2;
|
|
25338
25846
|
var init_SessionsView = __esm({
|
|
25339
25847
|
"src/tui/SessionsView.tsx"() {
|
|
25340
25848
|
"use strict";
|
|
25341
25849
|
init_Table();
|
|
25342
|
-
|
|
25850
|
+
COL_AUTHORING = 16;
|
|
25343
25851
|
COL_UPDATED2 = 10;
|
|
25344
25852
|
COL_NAME2 = 28;
|
|
25345
25853
|
}
|
|
@@ -25347,7 +25855,7 @@ var init_SessionsView = __esm({
|
|
|
25347
25855
|
|
|
25348
25856
|
// src/tui/SpecInspector.tsx
|
|
25349
25857
|
import { Box as Box12, Text as Text13 } from "ink";
|
|
25350
|
-
import { stringify as
|
|
25858
|
+
import { stringify as stringify6 } from "yaml";
|
|
25351
25859
|
import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
25352
25860
|
function SpecInspector({
|
|
25353
25861
|
kind,
|
|
@@ -25378,7 +25886,7 @@ function specLineCount(spec) {
|
|
|
25378
25886
|
return yamlLines(spec).length;
|
|
25379
25887
|
}
|
|
25380
25888
|
function yamlLines(spec) {
|
|
25381
|
-
return
|
|
25889
|
+
return stringify6(spec).trimEnd().split("\n");
|
|
25382
25890
|
}
|
|
25383
25891
|
function YamlLine({ line }) {
|
|
25384
25892
|
const match = /^(\s*)([^:#][^:]*:)(.*)$/.exec(line);
|
|
@@ -25465,6 +25973,15 @@ function selectedNamedResourceForSection(input) {
|
|
|
25465
25973
|
return null;
|
|
25466
25974
|
}
|
|
25467
25975
|
}
|
|
25976
|
+
function agentAuthoringHeaderLabel(status) {
|
|
25977
|
+
if (!status.ok) {
|
|
25978
|
+
return "error";
|
|
25979
|
+
}
|
|
25980
|
+
if (status.imports === 0 && status.removals === 0) {
|
|
25981
|
+
return "local ok";
|
|
25982
|
+
}
|
|
25983
|
+
return `local ok, ${status.imports} imports, ${status.removals} removals`;
|
|
25984
|
+
}
|
|
25468
25985
|
function editableResourceForSelection(input) {
|
|
25469
25986
|
if (!isApplyResourceSection(input.activeSection)) {
|
|
25470
25987
|
return null;
|
|
@@ -25523,6 +26040,15 @@ function HomeView({ apiUrl, notice, returnToSession }) {
|
|
|
25523
26040
|
const [serviceAccountNameInput, setServiceAccountNameInput] = useState3("");
|
|
25524
26041
|
const [inspectedResource, setInspectedResource] = useState3(null);
|
|
25525
26042
|
const [inspectScrollOffset, setInspectScrollOffset] = useState3(0);
|
|
26043
|
+
const localAgentAuthoringStatuses = useMemo3(
|
|
26044
|
+
() => new Map(
|
|
26045
|
+
readLocalAgentAuthoringStatuses().map((status) => [
|
|
26046
|
+
status.name,
|
|
26047
|
+
status
|
|
26048
|
+
])
|
|
26049
|
+
),
|
|
26050
|
+
[]
|
|
26051
|
+
);
|
|
25526
26052
|
const activeSelectionIds = client.getActiveSelection();
|
|
25527
26053
|
const {
|
|
25528
26054
|
data: sessions = [],
|
|
@@ -25625,6 +26151,7 @@ function HomeView({ apiUrl, notice, returnToSession }) {
|
|
|
25625
26151
|
const selectedConnectionProvider = providerRows2(connectionProviders)[connectionProviderIndex];
|
|
25626
26152
|
const selectedServiceAccount = serviceAccounts[serviceAccountIndex];
|
|
25627
26153
|
const selectedSession = sessions[sessionIndex];
|
|
26154
|
+
const selectedAgentAuthoringStatus = selectedSession ? localAgentAuthoringStatuses.get(selectedSession.metadata.name) : void 0;
|
|
25628
26155
|
const selectedEnvironment = environments[environmentIndex];
|
|
25629
26156
|
const selectedIdentity = identities[identityIndex];
|
|
25630
26157
|
const selectedInspectableResource = useMemo3(() => {
|
|
@@ -26505,7 +27032,7 @@ function HomeView({ apiUrl, notice, returnToSession }) {
|
|
|
26505
27032
|
}
|
|
26506
27033
|
});
|
|
26507
27034
|
if (isLoading) {
|
|
26508
|
-
return /* @__PURE__ */ jsx14(Box13, { height: termHeight, alignItems: "center", justifyContent: "center", children: /* @__PURE__ */ jsx14(Spinner, { label: "loading
|
|
27035
|
+
return /* @__PURE__ */ jsx14(Box13, { height: termHeight, alignItems: "center", justifyContent: "center", children: /* @__PURE__ */ jsx14(Spinner, { label: "loading agents\u2026" }) });
|
|
26509
27036
|
}
|
|
26510
27037
|
if (authErrorMessage && sessions.length === 0 && !projectSelectionRequired) {
|
|
26511
27038
|
return /* @__PURE__ */ jsx14(
|
|
@@ -26526,7 +27053,7 @@ function HomeView({ apiUrl, notice, returnToSession }) {
|
|
|
26526
27053
|
let sectionTitle;
|
|
26527
27054
|
switch (activeSection) {
|
|
26528
27055
|
case "sessions":
|
|
26529
|
-
sectionTitle = "
|
|
27056
|
+
sectionTitle = "Agents";
|
|
26530
27057
|
break;
|
|
26531
27058
|
case "projects":
|
|
26532
27059
|
sectionTitle = `Projects[${projects.length}]`;
|
|
@@ -26547,7 +27074,7 @@ function HomeView({ apiUrl, notice, returnToSession }) {
|
|
|
26547
27074
|
case "runs": {
|
|
26548
27075
|
const selectedSuffix = markedRunIds.length > 0 ? `, ${markedRunIds.length} selected` : "";
|
|
26549
27076
|
if (scopedSession) {
|
|
26550
|
-
sectionTitle = showArchivedRuns ? `
|
|
27077
|
+
sectionTitle = showArchivedRuns ? `Agent Runs[${scopedSession.metadata.name}: ${visibleRuns.length} archived${selectedSuffix}]` : `Agent Runs[${scopedSession.metadata.name}: ${visibleRuns.length}${selectedSuffix}]`;
|
|
26551
27078
|
} else {
|
|
26552
27079
|
sectionTitle = showArchivedRuns ? `Runs[${visibleRuns.length} archived${selectedSuffix}]` : `Runs[${visibleRuns.length}${selectedSuffix}]`;
|
|
26553
27080
|
}
|
|
@@ -26571,7 +27098,15 @@ function HomeView({ apiUrl, notice, returnToSession }) {
|
|
|
26571
27098
|
activeProject && /* @__PURE__ */ jsx14(KV, { label: "Org", value: activeProject.organizationName }),
|
|
26572
27099
|
activeProject && /* @__PURE__ */ jsx14(KV, { label: "Project", value: activeProject.projectName }),
|
|
26573
27100
|
activeProject && /* @__PURE__ */ jsx14(KV, { label: "Role", value: activeProject.role }),
|
|
26574
|
-
selectedSession && /* @__PURE__ */ jsx14(KV, { label: "
|
|
27101
|
+
selectedSession && /* @__PURE__ */ jsx14(KV, { label: "Agent", value: selectedSession.metadata.name }),
|
|
27102
|
+
selectedAgentAuthoringStatus && /* @__PURE__ */ jsx14(
|
|
27103
|
+
KV,
|
|
27104
|
+
{
|
|
27105
|
+
label: "Authoring",
|
|
27106
|
+
value: agentAuthoringHeaderLabel(selectedAgentAuthoringStatus),
|
|
27107
|
+
color: selectedAgentAuthoringStatus.ok ? void 0 : "red"
|
|
27108
|
+
}
|
|
27109
|
+
),
|
|
26575
27110
|
runError && /* @__PURE__ */ jsx14(KV, { label: "Error", value: runError, color: "red" }),
|
|
26576
27111
|
isRunMutationPending && /* @__PURE__ */ jsx14(
|
|
26577
27112
|
KV,
|
|
@@ -26720,7 +27255,8 @@ function HomeView({ apiUrl, notice, returnToSession }) {
|
|
|
26720
27255
|
{
|
|
26721
27256
|
sessions,
|
|
26722
27257
|
selectedIndex: sessionIndex,
|
|
26723
|
-
error: sessionsErrorMessage
|
|
27258
|
+
error: sessionsErrorMessage,
|
|
27259
|
+
authoringStatuses: localAgentAuthoringStatuses
|
|
26724
27260
|
}
|
|
26725
27261
|
) : activeSection === "serviceAccounts" ? isServiceAccountsLoading ? /* @__PURE__ */ jsx14(Box13, { padding: 2, children: /* @__PURE__ */ jsx14(Spinner, { label: "loading service accounts\u2026" }) }) : /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", flexGrow: 1, children: [
|
|
26726
27262
|
serviceAccountInputMode === "create" && /* @__PURE__ */ jsxs12(Box13, { paddingX: 2, gap: 1, children: [
|
|
@@ -27039,6 +27575,7 @@ var init_HomeView = __esm({
|
|
|
27039
27575
|
"src/tui/HomeView.tsx"() {
|
|
27040
27576
|
"use strict";
|
|
27041
27577
|
init_src();
|
|
27578
|
+
init_authoring();
|
|
27042
27579
|
init_login();
|
|
27043
27580
|
init_base_url();
|
|
27044
27581
|
init_browser();
|
|
@@ -27746,8 +28283,8 @@ async function startGitCredentialRelay(input) {
|
|
|
27746
28283
|
update: (next) => {
|
|
27747
28284
|
target = { url: next.url, accessToken: next.accessToken };
|
|
27748
28285
|
},
|
|
27749
|
-
close: () => new Promise((
|
|
27750
|
-
server.close(() =>
|
|
28286
|
+
close: () => new Promise((resolve3) => {
|
|
28287
|
+
server.close(() => resolve3());
|
|
27751
28288
|
})
|
|
27752
28289
|
};
|
|
27753
28290
|
}
|
|
@@ -28170,12 +28707,12 @@ async function runAgentBridgeSocket(options) {
|
|
|
28170
28707
|
let handler = null;
|
|
28171
28708
|
const reconnectLoop = createReconnectLoop(socket);
|
|
28172
28709
|
let hasConnected = false;
|
|
28173
|
-
await new Promise((
|
|
28710
|
+
await new Promise((resolve3, reject) => {
|
|
28174
28711
|
const shutdown = () => {
|
|
28175
28712
|
reconnectLoop.stop();
|
|
28176
28713
|
handler?.shutdown?.();
|
|
28177
28714
|
socket.disconnect();
|
|
28178
|
-
|
|
28715
|
+
resolve3();
|
|
28179
28716
|
};
|
|
28180
28717
|
process.once("SIGINT", shutdown);
|
|
28181
28718
|
process.once("SIGTERM", shutdown);
|
|
@@ -28358,7 +28895,7 @@ function createRuntimeBridgeBootstrapListener(input) {
|
|
|
28358
28895
|
};
|
|
28359
28896
|
}
|
|
28360
28897
|
function emitOutputWithAck(socket, output) {
|
|
28361
|
-
return new Promise((
|
|
28898
|
+
return new Promise((resolve3, reject) => {
|
|
28362
28899
|
socket.timeout(5e3).emit(
|
|
28363
28900
|
RUNTIME_BRIDGE_OUTPUT_EVENT,
|
|
28364
28901
|
output,
|
|
@@ -28378,7 +28915,7 @@ function emitOutputWithAck(socket, output) {
|
|
|
28378
28915
|
);
|
|
28379
28916
|
return;
|
|
28380
28917
|
}
|
|
28381
|
-
|
|
28918
|
+
resolve3(ack.data);
|
|
28382
28919
|
}
|
|
28383
28920
|
);
|
|
28384
28921
|
});
|
|
@@ -29121,8 +29658,8 @@ var AsyncMessageQueue = class {
|
|
|
29121
29658
|
if (this.closed) {
|
|
29122
29659
|
return Promise.resolve({ done: true, value: void 0 });
|
|
29123
29660
|
}
|
|
29124
|
-
return new Promise((
|
|
29125
|
-
this.waiters.push(
|
|
29661
|
+
return new Promise((resolve3) => {
|
|
29662
|
+
this.waiters.push(resolve3);
|
|
29126
29663
|
});
|
|
29127
29664
|
}
|
|
29128
29665
|
};
|
|
@@ -29386,13 +29923,13 @@ var ClaudeCodeCommandHandler = class {
|
|
|
29386
29923
|
this.input.writeOutput?.(
|
|
29387
29924
|
`agent_bridge_question_pending tool_use_id=${toolUseId}`
|
|
29388
29925
|
);
|
|
29389
|
-
return new Promise((
|
|
29390
|
-
this.pendingQuestions.set(toolUseId, { input: toolInput, resolve:
|
|
29926
|
+
return new Promise((resolve3) => {
|
|
29927
|
+
this.pendingQuestions.set(toolUseId, { input: toolInput, resolve: resolve3 });
|
|
29391
29928
|
options.signal.addEventListener(
|
|
29392
29929
|
"abort",
|
|
29393
29930
|
() => {
|
|
29394
29931
|
if (this.pendingQuestions.delete(toolUseId)) {
|
|
29395
|
-
|
|
29932
|
+
resolve3({
|
|
29396
29933
|
behavior: "deny",
|
|
29397
29934
|
message: "The question was cancelled before the user answered",
|
|
29398
29935
|
toolUseID: toolUseId
|
|
@@ -30041,7 +30578,7 @@ async function activeGrantIds(input) {
|
|
|
30041
30578
|
);
|
|
30042
30579
|
}
|
|
30043
30580
|
async function waitForNewGrant(input, knownGrantIds) {
|
|
30044
|
-
const sleep3 = input.sleep ?? ((ms) => new Promise((
|
|
30581
|
+
const sleep3 = input.sleep ?? ((ms) => new Promise((resolve3) => setTimeout(resolve3, ms)));
|
|
30045
30582
|
const now3 = input.now ?? Date.now;
|
|
30046
30583
|
const deadline = now3() + (input.pollTimeoutMs ?? POLL_TIMEOUT_MS);
|
|
30047
30584
|
while (now3() < deadline) {
|
|
@@ -30738,13 +31275,13 @@ function registerDeleteCommands(program, context) {
|
|
|
30738
31275
|
|
|
30739
31276
|
// src/commands/describe/actions.ts
|
|
30740
31277
|
init_resources3();
|
|
30741
|
-
import { stringify } from "yaml";
|
|
31278
|
+
import { stringify as stringify2 } from "yaml";
|
|
30742
31279
|
async function inspectResource(input) {
|
|
30743
31280
|
const request = parseProjectResourceReference(input.resource);
|
|
30744
31281
|
const resource = await requireProjectResource(input.client, request, {
|
|
30745
31282
|
apiBaseUrl: input.commandOptions.apiBaseUrl
|
|
30746
31283
|
});
|
|
30747
|
-
input.writeOutput(
|
|
31284
|
+
input.writeOutput(stringify2(resource.spec).trimEnd());
|
|
30748
31285
|
}
|
|
30749
31286
|
|
|
30750
31287
|
// src/commands/describe/commands.ts
|
|
@@ -30881,7 +31418,7 @@ var humanQuickstartText = `Get started with auto:
|
|
|
30881
31418
|
1. auto auth login sign in (device flow; account setup in browser)
|
|
30882
31419
|
2. auto connections list see available providers; auto connect <provider>
|
|
30883
31420
|
3. auto apply apply .auto/ resources to your project
|
|
30884
|
-
4. auto run <
|
|
31421
|
+
4. auto run <agent> launch an agent run; add --attach to follow it
|
|
30885
31422
|
|
|
30886
31423
|
Fastest path: paste this into a coding agent running in your repo
|
|
30887
31424
|
(Claude Code, Cursor, Codex):
|
|
@@ -30895,7 +31432,7 @@ Docs and help: auto --help
|
|
|
30895
31432
|
`;
|
|
30896
31433
|
|
|
30897
31434
|
// src/commands/onboard/skill-content.generated.ts
|
|
30898
|
-
var onboardingSkillMarkdown = "# Intent\n\nYou are onboarding a user onto auto. Achieve three goals, in roughly this order, as rapidly as the user's pace allows:\n\n1. **Educate** \u2014 teach the user what auto is and how it works, and get them genuinely excited about it.\n2. **Magic moment** \u2014 get a tailor-made, deployed, proactive workflow live that solves a *real* problem for them, and have them witness it working end to end.\n3. **Self-sufficiency** \u2014 leave them with the building blocks (mental model, CI/CD, a self-improvement loop) to iterate on their auto system rapidly and safely on their own.\n\n# Background\n\n**What is auto?**\n\nauto lets you program software factories the same way you program CI/CD.\n\nCompose agents and triggers into workflows using simple YAML files, and deploy them into the cloud on merge.\n\nYou can use auto to build simple (but effective) automations:\n\n- Ticket / feedback triage and resolution\n- Automated incident / bug response\n- Custom tailored code review agents\n\nYou can also use auto to push the frontier of agentic labor:\n\n- Organized fleets of agents on long-horizon tasks\n- Multi-agent autoresearch / optimization loops\n- Agentic BDR and outbound lead engines\n- \u221E more ideas we've yet to dream up\n\nAnything that can be described in a standard operating procedure can be translated into a \"chart\" of agents and triggers in auto \u2014 the only limit is your imagination.\n\n# Reference material\n\nThis skill ships with documentation and worked examples. Read them before you onboard anyone; cite and copy from them as you go.\n\n| Path | What it covers |\n| --- | --- |\n| `docs/index.md` | The mental model: resources, events, triggers, runs. Start here. |\n| `docs/resource-model.md` | The `.auto/` directory, resource envelopes, and `auto apply` semantics. |\n| `docs/sessions-and-triggers.md` | Sessions, the trigger/event/routing vocabulary, filters, and PR checks. |\n| `docs/environments-and-profiles.md` | Sandbox images, setup steps and caching, and reusable agent profiles. |\n| `docs/tools-and-connections.md` | MCP tools, chat tools, provider connections, secrets, and the runtime tool surface agents see. |\n| `docs/cli.md` | The `auto` CLI command reference. |\n| `docs/ci-cd.md` | Service accounts and GitHub Actions for apply-on-merge. |\n| `examples/index.md` | Prose outline of every example \u2014 read this to know what's on the shelf. |\n| `examples/` | Complete, copyable `.auto/` directories \u2014 one per workflow archetype, each with a README explaining the moving parts. |\n\nIf these relative paths are not available (for example this playbook was printed by `auto onboard --agent` rather than installed as a skill directory), fetch the same content from the skills mirror: `npx skills add auto-dot-sh/skills`, or browse https://github.com/auto-dot-sh/skills.\n\n# Operating principles\n\nHold these throughout the onboarding:\n\n- **Trust live command output over this document.** The CLI evolves; run `auto --help` early and whenever in doubt, and when a command's real output disagrees with anything written here, trust the command output over this document and adapt.\n- **Converse, don't lecture.** Short messages, one question at a time, and adapt your vocabulary to the user's technical level. The pitch should take seconds, not paragraphs.\n- **Ask before changing anything outside `.auto/`.** The onboarding's write surface is the `.auto/` directory (plus the CI workflow in Beat 7, which ships as a PR). Any other file in the user's repo gets touched only with their explicit go-ahead.\n- **Warn before browsers open, and surface the link either way.** `auto auth login`, `auto connect`, and `auto sessions connect` open a browser window *and* print the authorization URL. Give a one-sentence heads-up first (\"this will open your browser to install the GitHub App\") so it doesn't feel like something hijacked their machine. If the browser doesn't pop (some environments can't open one), don't leave the user hunting through command output \u2014 repeat the printed authorization URL back to them on its own line as a clickable fallback, one provider at a time, and tell them plainly to click it.\n- **Signal before going quiet.** Deep repo exploration and waiting on async runs both involve silence. Say what you're about to do and roughly how long it will take.\n- **Enlist the user as the second pair of hands.** They trigger the inputs you can't (tagging a bot in Slack, commenting on a PR) and verify the outputs you can't see (a Slack message arriving). Make those asks explicit and specific.\n- **Hand off, don't hint.** When the user needs to do something, spell it out the *first* time \u2014 before they have to ask. Name the exact trigger (which label, which channel, which command), where to click, and what they'll see when it works. \"Label the issue whenever you're ready\" assumes they can see what's in your head and the YAML you wrote; a numbered \"in Linear: create an issue \u2192 add the `auto-triage` label \u2192 that label is the trigger\" does not. If you catch yourself about to post a one-line \"go ahead and \u2026\", expand it.\n- **Set expectations once, then stay quiet.** When you start watching an async run, tell the user up front roughly how long it takes and what \"normal\" looks like (\"the coder run provisions a sandbox first \u2014 expect a quiet couple of minutes\"), then hold until something *they'd care about* changes. Don't narrate every monitor tick or re-report the same event from a second watcher \u2014 a stream of \"still queued / still running / no news\" reads as noise, not reassurance.\n- **Expect trouble; own the troubleshooting.** OAuth flows fail, secrets get mistyped, webhooks misfire. When something breaks, diagnose it with the CLI (`auto runs list`, `auto runs show`, `auto runs conversation`, `auto apply --dry-run`) rather than asking the user to debug.\n- **Asynchronous means asynchronous.** Triggered runs take time to spawn and act. Tell the user when a wait is expected, and tail run state rather than declaring failure early.\n- **Never fabricate success.** Verify each step actually worked (the apply plan, the trigger receipt, the run conversation) before telling the user it did.\n- **Celebrate real wins.** When a workflow completes end to end for the first time, mark the moment \u2014 emoji, a pun, a little flourish. This should feel fun.\n\n# Procedure\n\nWork through the following beats in order. They are a roadmap, not a script \u2014 skip or reorder when the user's situation clearly calls for it (for example, a user who already has an account and connections can jump straight to Beat 3).\n\n## Beat 0: Learn auto\n\nBefore talking to the user, make sure you have a working command of the system: read `docs/index.md` for the mental model, skim the rest of `docs/`, and look through `examples/` to internalize what complete workflows look like. You will be drawing on the examples heavily in Beats 3-5.\n\n## Beat 1: Establish rapport\n\n**Your very first message after launching is a plain-language pitch, not a form.** Two or three sentences on what auto is and where it's valuable, then *one* opening question. Do **not** open with `AskUserQuestion` or a multiple-choice menu \u2014 that skips the *Educate* goal and makes the onboarding feel like a config wizard. Lead with words; reach for `AskUserQuestion` only once you're past the pitch and genuinely offering discrete choices (e.g. the hero workflow in Beat 3).\n\nAfter the pitch, shift into lightly interviewing the user. You want to learn:\n\n1. **Who they are and their professional context.**\n - Hobbyist, or evaluating auto for a real business?\n - How technical are they? Engineer, or a more managerial / operational role?\n2. **Where the work that matters most to them happens.**\n - Do they have a GitHub account / organization? Is there a repo that would make a good home for their auto system \u2014 better yet, are you running inside it right now?\n - Do they work out of Slack day-to-day, and could they install auto there?\n - What else is in their operating loop? Linear, Datadog, Sentry, PostHog, Notion, Telegram, internal webhooks, and so on.\n\nKeep this light \u2014 a few questions, not a survey. You're gathering enough signal to propose workflows that will land.\n\n## Beat 2: Get up to speed\n\nIf you are running inside a repo the user has indicated is their focus, tell them you're going to explore it for a few minutes (and that you'll go quiet while a research agent reads the repo) \u2014 then **dispatch a subagent to do the deep read in parallel** rather than reading file-by-file in the main thread. This keeps the conversation responsive and your own context clean, and it forces real exploration instead of leaning on whatever `CLAUDE.md` / `AGENTS.md` happened to load.\n\nSpawn one general-purpose / Explore subagent (or a small fan-out of them for a large monorepo) and have it read **both**:\n\n- **The repo:** what the project does, how the team works (CI, review culture, issue-tracker and chat integrations), the conventions written down in `CLAUDE.md`/`AGENTS.md`/`docs/`, and \u2014 most importantly \u2014 where the recurring, automatable toil is.\n- **This skill's `docs/` and `examples/`**, so the ideas it returns are already expressed in auto's vocabulary (sessions, triggers, profiles) and mapped to a concrete archetype.\n\nHave the subagent return a structured shortlist: for each candidate workflow, a one-line description, the matching archetype, the trigger/event that would fire it, and the *specific evidence in this repo* that the toil is real (a file, a workflow, a documented rule, a past incident). That shortlist is the raw material for Beat 3.\n\nWhen the agent returns, don't just move on \u2014 **surface 1-2 concrete observations to the user** (\"you renumber migrations by hand and a missed renumber caused a prod outage; your `postman/collection.json` updates are marked NOT OPTIONAL\") so they see the exploration paid off and trust that your pitches are grounded in *their* code. If `CLAUDE.md` already told you something, say so and confirm it against the repo rather than presenting it as discovery.\n\n## Beat 3: Present some options\n\nCombine what you know about the user, their goals, and their codebase, and brainstorm at least three workflows they could deploy *today*. Anchor on the archetypes in `examples/index.md` \u2014 code review, issue triage, incident response, chat assistant, scheduled digest, an orchestrated agent fleet, a research/optimization loop, an outbound lead engine \u2014 but tailor each pitch to their actual stack and pain points (\"a review agent that enforces *your* `docs/style.md`\", not \"a code review bot\"). The archetypes are anchors, not a menu: if the user's situation suggests a useful workflow that matches none of them, it is absolutely fair game \u2014 pitch it. Calibrate ambition to the user: the simple automations land the magic moment fastest, while the frontier examples (fleet, research loop) make better second acts unless the user is clearly hungry for them.\n\nPresent the options as a question, one line each on what the workflow would do for them, and let them pick \u2014 including the option to propose their own idea instead. The winner becomes the hero use case.\n\n## Beat 4: Setup & smoke test\n\nGet the user from zero to a deployed, *hollow* version of the hero workflow \u2014 a shell that proves every input and output is wired up before you invest in the real logic. In practice:\n\n1. **Install the CLI**: `npm install -g @autohq/cli` (requires Node 20+). Verify with `auto --version`.\n2. **Sign in**: `auto auth login` (heads-up: opens a browser; account creation happens there too). You're blocked on the user completing the flow either way, so wait for them \u2014 don't busy yourself with other work mid-sign-in, which only confuses things. When you're driving from a terminal with no browser, `auto auth login --device` prints a code the user enters in their browser.\n3. **Create the org and project**: `auto orgs create` / `auto projects create`. Ask the user what they want to name them \u2014 don't pick names for them.\n4. **Connect providers**: `auto connections list --available` to see what's offered, then `auto connect <provider>` for each one the workflow needs (heads-up: browser again). GitHub connects as an App installation; Slack and Linear as OAuth grants.\n5. **Scaffold `.auto/`**: create the directory in their repo and draft the minimal resources \u2014 an environment, a profile, any tool definitions, and a session with the workflow's trigger. Copy from the matching example and strip it down.\n6. **Apply**: `auto apply --dry-run` first, show the user the plan, then `auto apply`.\n\nThen run the smoke test. Its exact shape depends on the use case, but the goal is always the same: verify that the trigger fires and the agent's output surfaces reach the user. A workflow almost always involves some communication channel, so a good smoke test \"breaks the fourth wall\" \u2014 have the hollow agent send the user a hello in Slack (or wherever they live).\n\nEnlist the user, and **hand off, don't hint** (see the operating principle): when you ask them to fire the input only they can fire, give the full, numbered steps the first time \u2014 *which* label on *which* issue, *which* channel to create, the exact command to run, and what they'll see when it lands. Don't post \"go ahead and label the issue\" and assume they know a label is the trigger; that one-liner is what makes a user ask \"wait, what exactly do I do?\". Right after `auto apply`, before you start watching, tell them in plain words what just deployed and what their next action is. Then **set expectations once** \u2014 \"the run takes a minute or two to spawn; I'll tell you when it acts\" \u2014 and watch progress yourself with `auto runs list` and `auto attach <run-id>` (live stream; `auto runs conversation <run-id>` for a snapshot), surfacing only meaningful changes rather than every tick. Troubleshoot until the smoke test passes.\n\nIf a channel install is blocked \u2014 for example the Slack workspace requires admin approval \u2014 don't stall the onboarding on it. Pick an output surface the user can verify without the channel (a PR comment, a GitHub check, the run transcript via `auto runs conversation`), continue the beats, and circle back to realize the channel identity once the approval lands.\n\n## Beat 5: Build the real thing\n\nWith inputs and outputs proven, flesh the workflow out to its real form in `.auto/` \u2014 the full profile instructions, the real prompt, the filters and routing that make it production-shaped. Tell the user what you're changing, then apply it.\n\nTest end to end: trigger the workflow for real, follow the run, and enlist the user again for out-of-band inputs and output verification. Iterate until you've witnessed one complete, successful run of the real workflow.\n\nThen celebrate. This is the magic moment \u2014 act like it. \u{1F389}\n\n## Beat 6: Bring the user up to speed\n\nWalk the user through what you built, piece by piece: which environment, profile, tools, session, and triggers you composed, how an event flows through them to become a run, and where each file lives in `.auto/`. Show short snippets from the actual files rather than describing them abstractly.\n\nThen ask: anything they want to dig into further, or shall we set up CI/CD?\n\n## Beat 7: Set up CI/CD\n\nMake merges to their default branch the deployment mechanism for their auto system (this is the \"program software factories like CI/CD\" promise made literal). Following `docs/ci-cd.md`:\n\n1. Create a service account: have the *user* run `auto service-account create ci-apply --preset applier` in their own terminal (and a second `--preset read-only` account for PR dry-runs if they want plan-on-PR). The token prints exactly once and goes straight into a repo secret \u2014 it must never be pasted into the conversation, and if you run the command yourself it lands in your transcript.\n2. Add a GitHub Actions workflow that runs `auto apply --dry-run` on pull requests and `auto apply` on pushes to the default branch.\n3. Tell the user exactly which secret to create where in their repo settings (the service-account token, shown once at creation).\n4. Open a PR containing `.auto/` and the new workflow, and ask the user to merge it.\n\nWhen the merge lands, verify the apply ran cleanly in Actions, and congratulate them \u2014 their factory now ships itself.\n\n## Beat 8: Set up a self-improvement loop\n\nTell the user there's one last step we've found high-leverage: a workflow that watches their auto system itself \u2014 sweeping recent runs for failures, bottlenecks, and drift, and proposing improvements. Explain that it's just another auto workflow, fully theirs to tune.\n\nIf they're in, copy `examples/self-improvement/` and tailor it to their setup (their channel, their sessions, their cadence). Since CI/CD is now live, do **not** run `auto apply` yourself \u2014 open a PR and let them merge it. That's the new normal, and modeling it is the point.\n\n## Beat 9: Conclusion\n\nTell the user they're all set: a live workflow, CI/CD for their auto system, and a loop that helps it improve. Recap in two or three lines what now exists. Offer to help them build or optimize additional workflows \u2014 Beat 3's runner-up ideas are natural next candidates.\n";
|
|
31435
|
+
var onboardingSkillMarkdown = "# Intent\n\nYou are onboarding a user onto auto. Achieve three goals, in roughly this order, as rapidly as the user's pace allows:\n\n1. **Educate** \u2014 teach the user what auto is and how it works, and get them genuinely excited about it.\n2. **Magic moment** \u2014 get a tailor-made, deployed, proactive workflow live that solves a *real* problem for them, and have them witness it working end to end.\n3. **Self-sufficiency** \u2014 leave them with the building blocks (mental model, CI/CD, a self-improvement loop) to iterate on their auto system rapidly and safely on their own.\n\n# Background\n\n**What is auto?**\n\nauto lets you program software factories the same way you program CI/CD.\n\nCompose agents and triggers into workflows using simple YAML files, and deploy them into the cloud on merge.\n\nYou can use auto to build simple (but effective) automations:\n\n- Ticket / feedback triage and resolution\n- Automated incident / bug response\n- Custom tailored code review agents\n\nYou can also use auto to push the frontier of agentic labor:\n\n- Organized fleets of agents on long-horizon tasks\n- Multi-agent autoresearch / optimization loops\n- Agentic BDR and outbound lead engines\n- \u221E more ideas we've yet to dream up\n\nAnything that can be described in a standard operating procedure can be translated into a \"chart\" of agents and triggers in auto \u2014 the only limit is your imagination.\n\n# Reference material\n\nThis skill ships with documentation and worked examples. Read them before you onboard anyone; cite and copy from them as you go.\n\n| Path | What it covers |\n| --- | --- |\n| `docs/index.md` | The mental model: resources, events, triggers, runs. Start here. |\n| `docs/resource-model.md` | The `.auto/` directory, resource envelopes, and `auto apply` semantics. |\n| `docs/sessions-and-triggers.md` | Agents, the trigger/event/routing vocabulary, filters, and PR checks. |\n| `docs/environments-and-profiles.md` | Sandbox images, setup steps and caching, and reusable agent profiles. |\n| `docs/tools-and-connections.md` | MCP tools, chat tools, provider connections, secrets, and the runtime tool surface agents see. |\n| `docs/cli.md` | The `auto` CLI command reference. |\n| `docs/ci-cd.md` | Service accounts and GitHub Actions for apply-on-merge. |\n| `examples/index.md` | Prose outline of every example \u2014 read this to know what's on the shelf. |\n| `examples/` | Complete, copyable `.auto/` directories \u2014 one per workflow archetype, each with a README explaining the moving parts. |\n\nIf these relative paths are not available (for example this playbook was printed by `auto onboard --agent` rather than installed as a skill directory), fetch the same content from the skills mirror: `npx skills add auto-dot-sh/skills`, or browse https://github.com/auto-dot-sh/skills.\n\n# Operating principles\n\nHold these throughout the onboarding:\n\n- **Trust live command output over this document.** The CLI evolves; run `auto --help` early and whenever in doubt, and when a command's real output disagrees with anything written here, trust the command output over this document and adapt.\n- **Converse, don't lecture.** Short messages, one question at a time, and adapt your vocabulary to the user's technical level. The pitch should take seconds, not paragraphs.\n- **Ask before changing anything outside `.auto/`.** The onboarding's write surface is the `.auto/` directory (plus the CI workflow in Beat 7, which ships as a PR). Any other file in the user's repo gets touched only with their explicit go-ahead.\n- **Warn before browsers open, and surface the link either way.** `auto auth login`, `auto connect`, and `auto agents connect` open a browser window *and* print the authorization URL. Give a one-sentence heads-up first (\"this will open your browser to install the GitHub App\") so it doesn't feel like something hijacked their machine. If the browser doesn't pop (some environments can't open one), don't leave the user hunting through command output \u2014 repeat the printed authorization URL back to them on its own line as a clickable fallback, one provider at a time, and tell them plainly to click it.\n- **Signal before going quiet.** Deep repo exploration and waiting on async runs both involve silence. Say what you're about to do and roughly how long it will take.\n- **Enlist the user as the second pair of hands.** They trigger the inputs you can't (tagging a bot in Slack, commenting on a PR) and verify the outputs you can't see (a Slack message arriving). Make those asks explicit and specific.\n- **Hand off, don't hint.** When the user needs to do something, spell it out the *first* time \u2014 before they have to ask. Name the exact trigger (which label, which channel, which command), where to click, and what they'll see when it works. \"Label the issue whenever you're ready\" assumes they can see what's in your head and the YAML you wrote; a numbered \"in Linear: create an issue \u2192 add the `auto-triage` label \u2192 that label is the trigger\" does not. If you catch yourself about to post a one-line \"go ahead and \u2026\", expand it.\n- **Set expectations once, then stay quiet.** When you start watching an async run, tell the user up front roughly how long it takes and what \"normal\" looks like (\"the coder run provisions a sandbox first \u2014 expect a quiet couple of minutes\"), then hold until something *they'd care about* changes. Don't narrate every monitor tick or re-report the same event from a second watcher \u2014 a stream of \"still queued / still running / no news\" reads as noise, not reassurance.\n- **Expect trouble; own the troubleshooting.** OAuth flows fail, secrets get mistyped, webhooks misfire. When something breaks, diagnose it with the CLI (`auto runs list`, `auto runs show`, `auto runs conversation`, `auto apply --dry-run`) rather than asking the user to debug.\n- **Asynchronous means asynchronous.** Triggered runs take time to spawn and act. Tell the user when a wait is expected, and tail run state rather than declaring failure early.\n- **Never fabricate success.** Verify each step actually worked (the apply plan, the trigger receipt, the run conversation) before telling the user it did.\n- **Celebrate real wins.** When a workflow completes end to end for the first time, mark the moment \u2014 emoji, a pun, a little flourish. This should feel fun.\n\n# Procedure\n\nWork through the following beats in order. They are a roadmap, not a script \u2014 skip or reorder when the user's situation clearly calls for it (for example, a user who already has an account and connections can jump straight to Beat 3).\n\n## Beat 0: Learn auto\n\nBefore talking to the user, make sure you have a working command of the system: read `docs/index.md` for the mental model, skim the rest of `docs/`, and look through `examples/` to internalize what complete workflows look like. You will be drawing on the examples heavily in Beats 3-5.\n\n## Beat 1: Establish rapport\n\n**Your very first message after launching is a plain-language pitch, not a form.** Two or three sentences on what auto is and where it's valuable, then *one* opening question. Do **not** open with `AskUserQuestion` or a multiple-choice menu \u2014 that skips the *Educate* goal and makes the onboarding feel like a config wizard. Lead with words; reach for `AskUserQuestion` only once you're past the pitch and genuinely offering discrete choices (e.g. the hero workflow in Beat 3).\n\nAfter the pitch, shift into lightly interviewing the user. You want to learn:\n\n1. **Who they are and their professional context.**\n - Hobbyist, or evaluating auto for a real business?\n - How technical are they? Engineer, or a more managerial / operational role?\n2. **Where the work that matters most to them happens.**\n - Do they have a GitHub account / organization? Is there a repo that would make a good home for their auto system \u2014 better yet, are you running inside it right now?\n - Do they work out of Slack day-to-day, and could they install auto there?\n - What else is in their operating loop? Linear, Datadog, Sentry, PostHog, Notion, Telegram, internal webhooks, and so on.\n\nKeep this light \u2014 a few questions, not a survey. You're gathering enough signal to propose workflows that will land.\n\n## Beat 2: Get up to speed\n\nIf you are running inside a repo the user has indicated is their focus, tell them you're going to explore it for a few minutes (and that you'll go quiet while a research agent reads the repo) \u2014 then **dispatch a subagent to do the deep read in parallel** rather than reading file-by-file in the main thread. This keeps the conversation responsive and your own context clean, and it forces real exploration instead of leaning on whatever `CLAUDE.md` / `AGENTS.md` happened to load.\n\nSpawn one general-purpose / Explore subagent (or a small fan-out of them for a large monorepo) and have it read **both**:\n\n- **The repo:** what the project does, how the team works (CI, review culture, issue-tracker and chat integrations), the conventions written down in `CLAUDE.md`/`AGENTS.md`/`docs/`, and \u2014 most importantly \u2014 where the recurring, automatable toil is.\n- **This skill's `docs/` and `examples/`**, so the ideas it returns are already expressed in auto's vocabulary (agents, triggers, profiles) and mapped to a concrete archetype.\n\nHave the subagent return a structured shortlist: for each candidate workflow, a one-line description, the matching archetype, the trigger/event that would fire it, and the *specific evidence in this repo* that the toil is real (a file, a workflow, a documented rule, a past incident). That shortlist is the raw material for Beat 3.\n\nWhen the agent returns, don't just move on \u2014 **surface 1-2 concrete observations to the user** (\"you renumber migrations by hand and a missed renumber caused a prod outage; your `postman/collection.json` updates are marked NOT OPTIONAL\") so they see the exploration paid off and trust that your pitches are grounded in *their* code. If `CLAUDE.md` already told you something, say so and confirm it against the repo rather than presenting it as discovery.\n\n## Beat 3: Present some options\n\nCombine what you know about the user, their goals, and their codebase, and brainstorm at least three workflows they could deploy *today*. Anchor on the archetypes in `examples/index.md` \u2014 code review, issue triage, incident response, chat assistant, scheduled digest, an orchestrated agent fleet, a research/optimization loop, an outbound lead engine \u2014 but tailor each pitch to their actual stack and pain points (\"a review agent that enforces *your* `docs/style.md`\", not \"a code review bot\"). The archetypes are anchors, not a menu: if the user's situation suggests a useful workflow that matches none of them, it is absolutely fair game \u2014 pitch it. Calibrate ambition to the user: the simple automations land the magic moment fastest, while the frontier examples (fleet, research loop) make better second acts unless the user is clearly hungry for them.\n\nPresent the options as a question, one line each on what the workflow would do for them, and let them pick \u2014 including the option to propose their own idea instead. The winner becomes the hero use case.\n\n## Beat 4: Setup & smoke test\n\nGet the user from zero to a deployed, *hollow* version of the hero workflow \u2014 a shell that proves every input and output is wired up before you invest in the real logic. In practice:\n\n1. **Install the CLI**: `npm install -g @autohq/cli` (requires Node 20+). Verify with `auto --version`.\n2. **Sign in**: `auto auth login` (heads-up: opens a browser; account creation happens there too). You're blocked on the user completing the flow either way, so wait for them \u2014 don't busy yourself with other work mid-sign-in, which only confuses things. When you're driving from a terminal with no browser, `auto auth login --device` prints a code the user enters in their browser.\n3. **Create the org and project**: `auto orgs create` / `auto projects create`. Ask the user what they want to name them \u2014 don't pick names for them.\n4. **Connect providers**: `auto connections list --available` to see what's offered, then `auto connect <provider>` for each one the workflow needs (heads-up: browser again). GitHub connects as an App installation; Slack and Linear as OAuth grants.\n5. **Scaffold `.auto/`**: create the directory in their repo and draft the minimal resources \u2014 an environment, a profile, any tool definitions, and an agent with the workflow's trigger. Copy from the matching example and strip it down.\n6. **Apply**: `auto apply --dry-run` first, show the user the plan, then `auto apply`.\n\nThen run the smoke test. Its exact shape depends on the use case, but the goal is always the same: verify that the trigger fires and the agent's output surfaces reach the user. A workflow almost always involves some communication channel, so a good smoke test \"breaks the fourth wall\" \u2014 have the hollow agent send the user a hello in Slack (or wherever they live).\n\nEnlist the user, and **hand off, don't hint** (see the operating principle): when you ask them to fire the input only they can fire, give the full, numbered steps the first time \u2014 *which* label on *which* issue, *which* channel to create, the exact command to run, and what they'll see when it lands. Don't post \"go ahead and label the issue\" and assume they know a label is the trigger; that one-liner is what makes a user ask \"wait, what exactly do I do?\". Right after `auto apply`, before you start watching, tell them in plain words what just deployed and what their next action is. Then **set expectations once** \u2014 \"the run takes a minute or two to spawn; I'll tell you when it acts\" \u2014 and watch progress yourself with `auto runs list` and `auto attach <run-id>` (live stream; `auto runs conversation <run-id>` for a snapshot), surfacing only meaningful changes rather than every tick. Troubleshoot until the smoke test passes.\n\nIf a channel install is blocked \u2014 for example the Slack workspace requires admin approval \u2014 don't stall the onboarding on it. Pick an output surface the user can verify without the channel (a PR comment, a GitHub check, the run transcript via `auto runs conversation`), continue the beats, and circle back to realize the channel identity once the approval lands.\n\n## Beat 5: Build the real thing\n\nWith inputs and outputs proven, flesh the workflow out to its real form in `.auto/` \u2014 the full profile instructions, the real prompt, the filters and routing that make it production-shaped. Tell the user what you're changing, then apply it.\n\nTest end to end: trigger the workflow for real, follow the run, and enlist the user again for out-of-band inputs and output verification. Iterate until you've witnessed one complete, successful run of the real workflow.\n\nThen celebrate. This is the magic moment \u2014 act like it. \u{1F389}\n\n## Beat 6: Bring the user up to speed\n\nWalk the user through what you built, piece by piece: which environment, profile, tools, agent, and triggers you composed, how an event flows through them to become a run, and where each file lives in `.auto/`. Show short snippets from the actual files rather than describing them abstractly.\n\nThen ask: anything they want to dig into further, or shall we set up CI/CD?\n\n## Beat 7: Set up CI/CD\n\nMake merges to their default branch the deployment mechanism for their auto system (this is the \"program software factories like CI/CD\" promise made literal). Following `docs/ci-cd.md`:\n\n1. Create a service account: have the *user* run `auto service-account create ci-apply --preset applier` in their own terminal (and a second `--preset read-only` account for PR dry-runs if they want plan-on-PR). The token prints exactly once and goes straight into a repo secret \u2014 it must never be pasted into the conversation, and if you run the command yourself it lands in your transcript.\n2. Add a GitHub Actions workflow that runs `auto apply --dry-run` on pull requests and `auto apply` on pushes to the default branch.\n3. Tell the user exactly which secret to create where in their repo settings (the service-account token, shown once at creation).\n4. Open a PR containing `.auto/` and the new workflow, and ask the user to merge it.\n\nWhen the merge lands, verify the apply ran cleanly in Actions, and congratulate them \u2014 their factory now ships itself.\n\n## Beat 8: Set up a self-improvement loop\n\nTell the user there's one last step we've found high-leverage: a workflow that watches their auto system itself \u2014 sweeping recent runs for failures, bottlenecks, and drift, and proposing improvements. Explain that it's just another auto workflow, fully theirs to tune.\n\nIf they're in, copy `examples/self-improvement/` and tailor it to their setup (their channel, their agents, their cadence). Since CI/CD is now live, do **not** run `auto apply` yourself \u2014 open a PR and let them merge it. That's the new normal, and modeling it is the point.\n\n## Beat 9: Conclusion\n\nTell the user they're all set: a live workflow, CI/CD for their auto system, and a loop that helps it improve. Recap in two or three lines what now exists. Offer to help them build or optimize additional workflows \u2014 Beat 3's runner-up ideas are natural next candidates.\n";
|
|
30899
31436
|
|
|
30900
31437
|
// src/commands/onboard/commands.ts
|
|
30901
31438
|
function registerOnboardCommands(program, context) {
|
|
@@ -31421,13 +31958,13 @@ function delay(ms, signal) {
|
|
|
31421
31958
|
if (signal.aborted) {
|
|
31422
31959
|
return Promise.resolve();
|
|
31423
31960
|
}
|
|
31424
|
-
return new Promise((
|
|
31425
|
-
const timeout = setTimeout(
|
|
31961
|
+
return new Promise((resolve3) => {
|
|
31962
|
+
const timeout = setTimeout(resolve3, ms);
|
|
31426
31963
|
signal.addEventListener(
|
|
31427
31964
|
"abort",
|
|
31428
31965
|
() => {
|
|
31429
31966
|
clearTimeout(timeout);
|
|
31430
|
-
|
|
31967
|
+
resolve3();
|
|
31431
31968
|
},
|
|
31432
31969
|
{ once: true }
|
|
31433
31970
|
);
|
|
@@ -31452,11 +31989,11 @@ function pollUntilFailed(input) {
|
|
|
31452
31989
|
input.abort?.addEventListener("abort", cancel, { once: true });
|
|
31453
31990
|
const done = (async () => {
|
|
31454
31991
|
while (!cancelled) {
|
|
31455
|
-
await new Promise((
|
|
31456
|
-
sleepResolve =
|
|
31992
|
+
await new Promise((resolve3) => {
|
|
31993
|
+
sleepResolve = resolve3;
|
|
31457
31994
|
activeSleep = setTimeout(() => {
|
|
31458
31995
|
activeSleep = void 0;
|
|
31459
|
-
|
|
31996
|
+
resolve3();
|
|
31460
31997
|
}, interval);
|
|
31461
31998
|
});
|
|
31462
31999
|
if (cancelled) {
|
|
@@ -31793,7 +32330,7 @@ function delay2(ms) {
|
|
|
31793
32330
|
if (ms <= 0) {
|
|
31794
32331
|
return Promise.resolve();
|
|
31795
32332
|
}
|
|
31796
|
-
return new Promise((
|
|
32333
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
31797
32334
|
}
|
|
31798
32335
|
function createDiagnosticCollector(limit) {
|
|
31799
32336
|
const events = [];
|
|
@@ -32191,9 +32728,9 @@ function entryPreview(event, full) {
|
|
|
32191
32728
|
case "text":
|
|
32192
32729
|
return part.text;
|
|
32193
32730
|
case "tool_call":
|
|
32194
|
-
return `${part.name} ${
|
|
32731
|
+
return `${part.name} ${stringify4(part.input)}`;
|
|
32195
32732
|
case "tool_result":
|
|
32196
|
-
return
|
|
32733
|
+
return stringify4(part.output);
|
|
32197
32734
|
case "question":
|
|
32198
32735
|
return part.questions.map((question) => question.question).join(" ");
|
|
32199
32736
|
default:
|
|
@@ -32204,9 +32741,9 @@ function entryPreview(event, full) {
|
|
|
32204
32741
|
return full ? joined : clip(joined);
|
|
32205
32742
|
}
|
|
32206
32743
|
function preview(value) {
|
|
32207
|
-
return clip(singleLine(
|
|
32744
|
+
return clip(singleLine(stringify4(value)));
|
|
32208
32745
|
}
|
|
32209
|
-
function
|
|
32746
|
+
function stringify4(value) {
|
|
32210
32747
|
return typeof value === "string" ? value : JSON.stringify(value);
|
|
32211
32748
|
}
|
|
32212
32749
|
function singleLine(text) {
|
|
@@ -32221,7 +32758,7 @@ function collect(value, previous = []) {
|
|
|
32221
32758
|
return [...previous, value];
|
|
32222
32759
|
}
|
|
32223
32760
|
function registerRunCommands(program, context) {
|
|
32224
|
-
const runs = program.command("runs").description("Inspect
|
|
32761
|
+
const runs = program.command("runs").description("Inspect agent runs.");
|
|
32225
32762
|
runs.command("list").description("List runs for a session or the whole project.").option("--session <name>", "session name").option("--include-archived", "include archived runs").option("--status <status>", "filter by run status (repeatable)", collect).option("--since <iso-timestamp>", "only runs created after this time").option("--limit <count>", "maximum runs to return", parsePositiveInteger).option("--api-url <url>", "Auto API base URL").option("--api-base-url <url>", "Auto API base URL").action(async (options) => {
|
|
32226
32763
|
await handleRunsList(context, options.session, options);
|
|
32227
32764
|
});
|
|
@@ -32291,7 +32828,7 @@ function registerRunCommands(program, context) {
|
|
|
32291
32828
|
runs.command("stop").description("Stop live runs and shut down their agent sessions.").argument("<run-ids...>", "run id(s)").option("--reason <reason>", "reason recorded with the stop").option("--api-url <url>", "Auto API base URL").option("--api-base-url <url>", "Auto API base URL").action(async (runIds, commandOptions) => {
|
|
32292
32829
|
await stopRunsAction(context, runIds, commandOptions);
|
|
32293
32830
|
});
|
|
32294
|
-
runs.command("benchmark-startup").description("Launch
|
|
32831
|
+
runs.command("benchmark-startup").description("Launch an agent run and measure time until awaiting.").requiredOption("--session <name>", "agent name").option("-m, --message <message>", "initial message for the run").option(
|
|
32295
32832
|
"--interactive",
|
|
32296
32833
|
"start without the default initial prompt unless --message is provided"
|
|
32297
32834
|
).option(
|
|
@@ -32313,7 +32850,7 @@ function registerRunCommands(program, context) {
|
|
|
32313
32850
|
).option("--json", "write a single JSON result object").option("--api-url <url>", "Auto API base URL").option("--api-base-url <url>", "Auto API base URL").action(async (options) => {
|
|
32314
32851
|
await benchmarkStartupAction(context, options);
|
|
32315
32852
|
});
|
|
32316
|
-
program.command("interactive").description("Launch
|
|
32853
|
+
program.command("interactive").description("Launch an agent run, stream it, and send typed messages.").argument("<session>", "agent name").option("-m, --message <message>", "initial message for the run").option("--api-url <url>", "Auto API base URL").option("--api-base-url <url>", "Auto API base URL").option("--operator <name>", "operator name for attach/detach messages").option(
|
|
32317
32854
|
"--attach-message",
|
|
32318
32855
|
"send the automated attach message after creating the run"
|
|
32319
32856
|
).option("--no-attach-message", "do not send the automated attach message").option("--no-detach-message", "do not send the automated detach message").action(
|
|
@@ -32651,12 +33188,57 @@ function withApiBaseUrl2(context, commandOptions) {
|
|
|
32651
33188
|
};
|
|
32652
33189
|
}
|
|
32653
33190
|
|
|
33191
|
+
// src/commands/agents/actions.ts
|
|
33192
|
+
init_authoring();
|
|
33193
|
+
import { stringify as stringify5 } from "yaml";
|
|
33194
|
+
function renderAgent(input) {
|
|
33195
|
+
const result = compileAgent(input);
|
|
33196
|
+
input.writeOutput(renderCompiledAgentYaml(result));
|
|
33197
|
+
}
|
|
33198
|
+
function compileAgent(input) {
|
|
33199
|
+
const path2 = resolveAgentAuthoringPath({
|
|
33200
|
+
agent: input.agent,
|
|
33201
|
+
directory: input.options.directory
|
|
33202
|
+
});
|
|
33203
|
+
return compileAgentFile(path2);
|
|
33204
|
+
}
|
|
33205
|
+
function compileAgentCommand(input) {
|
|
33206
|
+
const result = compileAgent(input);
|
|
33207
|
+
input.writeOutput(
|
|
33208
|
+
input.options.json ? JSON.stringify(result.resource) : stringify5(result.resource).trimEnd()
|
|
33209
|
+
);
|
|
33210
|
+
}
|
|
33211
|
+
function explainAgent(input) {
|
|
33212
|
+
const result = compileAgent(input);
|
|
33213
|
+
input.writeOutput(
|
|
33214
|
+
input.options.json ? JSON.stringify(result) : renderAgentExplain(result)
|
|
33215
|
+
);
|
|
33216
|
+
}
|
|
33217
|
+
function statusAgents(input) {
|
|
33218
|
+
const statuses = readLocalAgentAuthoringStatuses({
|
|
33219
|
+
directory: input.options.directory
|
|
33220
|
+
});
|
|
33221
|
+
if (input.options.json) {
|
|
33222
|
+
input.writeOutput(JSON.stringify(statuses));
|
|
33223
|
+
return;
|
|
33224
|
+
}
|
|
33225
|
+
if (statuses.length === 0) {
|
|
33226
|
+
input.writeOutput("No local agent authoring files found.");
|
|
33227
|
+
return;
|
|
33228
|
+
}
|
|
33229
|
+
for (const status of statuses) {
|
|
33230
|
+
const state = status.ok ? "ok" : "error";
|
|
33231
|
+
const suffix = status.ok ? `imports=${status.imports} removals=${status.removals}` : status.error;
|
|
33232
|
+
input.writeOutput(`${state} ${status.name} ${suffix}`);
|
|
33233
|
+
}
|
|
33234
|
+
}
|
|
33235
|
+
|
|
32654
33236
|
// src/commands/sessions/connect.ts
|
|
32655
33237
|
init_resources2();
|
|
32656
33238
|
init_browser();
|
|
32657
33239
|
import { existsSync as existsSync3, mkdtempSync as mkdtempSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
32658
33240
|
import { homedir as homedir3, tmpdir as tmpdir2 } from "os";
|
|
32659
|
-
import { join as
|
|
33241
|
+
import { join as join6 } from "path";
|
|
32660
33242
|
var POLL_INTERVAL_MS2 = 2e3;
|
|
32661
33243
|
var POLL_TIMEOUT_MS2 = 5 * 6e4;
|
|
32662
33244
|
var SLACK_APPS_URL = "https://api.slack.com/apps";
|
|
@@ -32677,7 +33259,7 @@ async function connectSessionPresence2(input) {
|
|
|
32677
33259
|
}
|
|
32678
33260
|
for (const pending of result.pending) {
|
|
32679
33261
|
input.writeOutput(
|
|
32680
|
-
`connect
|
|
33262
|
+
`connect agent/${input.session} workspace/${pending.workspace} connection/${pending.connection}`
|
|
32681
33263
|
);
|
|
32682
33264
|
input.writeOutput(`authorization_url ${pending.authorizationUrl}`);
|
|
32683
33265
|
if (pending.suggestedUsername) {
|
|
@@ -32708,7 +33290,7 @@ async function connectSessionPresence2(input) {
|
|
|
32708
33290
|
);
|
|
32709
33291
|
return;
|
|
32710
33292
|
}
|
|
32711
|
-
const sleep3 = input.sleep ?? ((ms) => new Promise((
|
|
33293
|
+
const sleep3 = input.sleep ?? ((ms) => new Promise((resolve3) => setTimeout(resolve3, ms)));
|
|
32712
33294
|
const openBrowser2 = input.openBrowser ?? openBrowser;
|
|
32713
33295
|
const now3 = input.now ?? Date.now;
|
|
32714
33296
|
const pollIntervalMs = input.pollIntervalMs ?? POLL_INTERVAL_MS2;
|
|
@@ -32738,7 +33320,7 @@ async function connectSessionPresence2(input) {
|
|
|
32738
33320
|
}
|
|
32739
33321
|
if (!realized) {
|
|
32740
33322
|
throw new Error(
|
|
32741
|
-
telegram ? `Timed out waiting for the Telegram bot to be confirmed under ${pending.workspace}. Confirm the creation dialog in Telegram, then re-run \`auto
|
|
33323
|
+
telegram ? `Timed out waiting for the Telegram bot to be confirmed under ${pending.workspace}. Confirm the creation dialog in Telegram, then re-run \`auto agents connect ${input.session}\`.` : `Timed out waiting for the install in workspace ${pending.workspace}. Re-run \`auto agents connect ${input.session}\` to retry.`
|
|
32742
33324
|
);
|
|
32743
33325
|
}
|
|
32744
33326
|
}
|
|
@@ -32746,19 +33328,19 @@ async function connectSessionPresence2(input) {
|
|
|
32746
33328
|
}
|
|
32747
33329
|
function realizedIdentityLine(session, identity2) {
|
|
32748
33330
|
const handle = identity2.botUsername ? `persona/@${identity2.botUsername}` : `bot/${identity2.botUserId ?? ""}`;
|
|
32749
|
-
return `connected
|
|
33331
|
+
return `connected agent/${session} workspace/${identity2.workspace} ${handle}`;
|
|
32750
33332
|
}
|
|
32751
33333
|
function staleScopesLine(session, identity2) {
|
|
32752
33334
|
if (!identity2.missingScopes?.length) {
|
|
32753
33335
|
return void 0;
|
|
32754
33336
|
}
|
|
32755
|
-
return ` workspace "${identity2.workspace}" was installed before scopes ${identity2.missingScopes.join(", ")} were added; refresh with \`auto
|
|
33337
|
+
return ` workspace "${identity2.workspace}" was installed before scopes ${identity2.missingScopes.join(", ")} were added; refresh with \`auto agents connect ${session} --reconnect\``;
|
|
32756
33338
|
}
|
|
32757
33339
|
function manualGuidance(input) {
|
|
32758
33340
|
if (input.allTelegram) {
|
|
32759
|
-
return `Open each creation link and confirm the new bot in Telegram; Auto provisions it automatically. Re-run \`auto
|
|
33341
|
+
return `Open each creation link and confirm the new bot in Telegram; Auto provisions it automatically. Re-run \`auto agents connect ${input.session}\` (or check presence) to see it realize.`;
|
|
32760
33342
|
}
|
|
32761
|
-
return input.reconnect ? "Open each authorization URL to reinstall the agent app; the callback page confirms the refreshed tokens." : "Open each authorization URL to install the agent app, then re-run with no flags or check `
|
|
33343
|
+
return input.reconnect ? "Open each authorization URL to reinstall the agent app; the callback page confirms the refreshed tokens." : "Open each authorization URL to install the agent app, then re-run with no flags or check `agents connect` again.";
|
|
32762
33344
|
}
|
|
32763
33345
|
async function promptForIconUploads(input, options) {
|
|
32764
33346
|
const presence = await input.client.getSessionPresence(
|
|
@@ -32775,14 +33357,14 @@ async function promptForIconUploads(input, options) {
|
|
|
32775
33357
|
for (const identity2 of drifted) {
|
|
32776
33358
|
const settingsUrl = `${SLACK_APPS_URL}/${identity2.appId}/general`;
|
|
32777
33359
|
input.writeOutput(
|
|
32778
|
-
`Workspace "${identity2.workspace}" agent app icon does not match the
|
|
33360
|
+
`Workspace "${identity2.workspace}" agent app icon does not match the agent avatar yet. Slack has no app-icon API; upload the avatar image once under "Display Information" at ${settingsUrl}`
|
|
32779
33361
|
);
|
|
32780
33362
|
if (identity2.avatarUrl) {
|
|
32781
33363
|
input.writeOutput(`avatar image: ${identity2.avatarUrl}`);
|
|
32782
33364
|
}
|
|
32783
33365
|
if (!canPrompt) {
|
|
32784
33366
|
input.writeOutput(
|
|
32785
|
-
`Then re-run \`auto
|
|
33367
|
+
`Then re-run \`auto agents connect ${input.session}\` interactively to record the upload.`
|
|
32786
33368
|
);
|
|
32787
33369
|
continue;
|
|
32788
33370
|
}
|
|
@@ -32822,12 +33404,12 @@ async function promptForIconUploads(input, options) {
|
|
|
32822
33404
|
options
|
|
32823
33405
|
);
|
|
32824
33406
|
input.writeOutput(
|
|
32825
|
-
`recorded icon
|
|
33407
|
+
`recorded icon agent/${input.session} workspace/${identity2.workspace} sha256/${recorded.appliedIconSha256.slice(0, 12)}`
|
|
32826
33408
|
);
|
|
32827
33409
|
}
|
|
32828
33410
|
}
|
|
32829
33411
|
function stagedLocationLabel(stagedPath) {
|
|
32830
|
-
return stagedPath.startsWith(`${
|
|
33412
|
+
return stagedPath.startsWith(`${join6(homedir3(), "Downloads")}/`) ? "Downloads" : "the printed path";
|
|
32831
33413
|
}
|
|
32832
33414
|
async function stageAvatarImage(input) {
|
|
32833
33415
|
try {
|
|
@@ -32839,9 +33421,9 @@ async function stageAvatarImage(input) {
|
|
|
32839
33421
|
}
|
|
32840
33422
|
const contentType = response.headers.get("content-type") ?? "";
|
|
32841
33423
|
const extension = contentType.includes("jpeg") ? ".jpg" : ".png";
|
|
32842
|
-
const downloads =
|
|
32843
|
-
const directory = existsSync3(downloads) ? downloads : mkdtempSync2(
|
|
32844
|
-
const path2 =
|
|
33424
|
+
const downloads = join6(homedir3(), "Downloads");
|
|
33425
|
+
const directory = existsSync3(downloads) ? downloads : mkdtempSync2(join6(tmpdir2(), "auto-avatar-"));
|
|
33426
|
+
const path2 = join6(directory, `${input.session}-avatar${extension}`);
|
|
32845
33427
|
writeFileSync4(path2, Buffer.from(await response.arrayBuffer()));
|
|
32846
33428
|
return path2;
|
|
32847
33429
|
} catch {
|
|
@@ -32929,17 +33511,56 @@ async function launchRun(input) {
|
|
|
32929
33511
|
|
|
32930
33512
|
// src/commands/sessions/commands.ts
|
|
32931
33513
|
function registerSessionCommands(program, context) {
|
|
32932
|
-
program.command("run").description("Launch
|
|
33514
|
+
program.command("run").description("Launch an agent run on the Auto platform.").argument("<session>", "agent name").option("-m, --message <message>", "initial message for the run").option("-a, --attach", "stream the run conversation after launch").option("--api-url <url>", "Auto API base URL").option("--api-base-url <url>", "Auto API base URL").action(async (sessionName, commandOptions) => {
|
|
32933
33515
|
await launchRun({
|
|
32934
33516
|
sessionName,
|
|
32935
33517
|
commandOptions,
|
|
32936
33518
|
context
|
|
32937
33519
|
});
|
|
32938
33520
|
});
|
|
32939
|
-
const sessions = program.command("sessions").description("Manage
|
|
33521
|
+
const sessions = program.command("agents").alias("sessions").description("Manage Agent resources.");
|
|
33522
|
+
sessions.command("render").description("Render a local Agent authoring file after imports/removals.").argument("<agent>", "agent name or path").option(
|
|
33523
|
+
"--directory <directory>",
|
|
33524
|
+
"directory containing Auto resource files"
|
|
33525
|
+
).action((agent, commandOptions) => {
|
|
33526
|
+
renderAgent({
|
|
33527
|
+
agent,
|
|
33528
|
+
options: commandOptions,
|
|
33529
|
+
writeOutput: context.writeOutput
|
|
33530
|
+
});
|
|
33531
|
+
});
|
|
33532
|
+
sessions.command("compile").description("Compile a local Agent authoring file into an apply resource.").argument("<agent>", "agent name or path").option(
|
|
33533
|
+
"--directory <directory>",
|
|
33534
|
+
"directory containing Auto resource files"
|
|
33535
|
+
).option("--json", "print the compiled resource as JSON").action((agent, commandOptions) => {
|
|
33536
|
+
compileAgentCommand({
|
|
33537
|
+
agent,
|
|
33538
|
+
options: commandOptions,
|
|
33539
|
+
writeOutput: context.writeOutput
|
|
33540
|
+
});
|
|
33541
|
+
});
|
|
33542
|
+
sessions.command("explain").description("Explain a local Agent authoring file's import graph.").argument("<agent>", "agent name or path").option(
|
|
33543
|
+
"--directory <directory>",
|
|
33544
|
+
"directory containing Auto resource files"
|
|
33545
|
+
).option("--json", "print the import graph as JSON").action((agent, commandOptions) => {
|
|
33546
|
+
explainAgent({
|
|
33547
|
+
agent,
|
|
33548
|
+
options: commandOptions,
|
|
33549
|
+
writeOutput: context.writeOutput
|
|
33550
|
+
});
|
|
33551
|
+
});
|
|
33552
|
+
sessions.command("status").description("Validate local Agent authoring files.").option(
|
|
33553
|
+
"--directory <directory>",
|
|
33554
|
+
"directory containing Auto resource files"
|
|
33555
|
+
).option("--json", "print local authoring status as JSON").action((commandOptions) => {
|
|
33556
|
+
statusAgents({
|
|
33557
|
+
options: commandOptions,
|
|
33558
|
+
writeOutput: context.writeOutput
|
|
33559
|
+
});
|
|
33560
|
+
});
|
|
32940
33561
|
sessions.command("connect").description(
|
|
32941
|
-
"Connect
|
|
32942
|
-
).argument("<session>", "
|
|
33562
|
+
"Connect an agent's provider presence (e.g. install its Slack agent app)."
|
|
33563
|
+
).argument("<session>", "agent resource name").option("--manual", "print authorization URLs without opening a browser").option(
|
|
32943
33564
|
"--reconnect",
|
|
32944
33565
|
"re-run the install for already-connected workspaces (repairs stranded bot tokens)"
|
|
32945
33566
|
).option("--api-url <url>", "Auto API base URL").option("--api-base-url <url>", "Auto API base URL").action(
|
|
@@ -32955,9 +33576,7 @@ function registerSessionCommands(program, context) {
|
|
|
32955
33576
|
});
|
|
32956
33577
|
}
|
|
32957
33578
|
);
|
|
32958
|
-
program.command("run-and-attach").description(
|
|
32959
|
-
"Launch a session run and immediately stream its conversation."
|
|
32960
|
-
).argument("<session>", "session name").option("-m, --message <message>", "initial message for the run").option("--api-url <url>", "Auto API base URL").option("--api-base-url <url>", "Auto API base URL").action(async (sessionName, commandOptions) => {
|
|
33579
|
+
program.command("run-and-attach").description("Launch an agent run and immediately stream its conversation.").argument("<session>", "agent name").option("-m, --message <message>", "initial message for the run").option("--api-url <url>", "Auto API base URL").option("--api-base-url <url>", "Auto API base URL").action(async (sessionName, commandOptions) => {
|
|
32961
33580
|
await launchRun({
|
|
32962
33581
|
sessionName,
|
|
32963
33582
|
commandOptions: {
|