forked from aws/aws-toolkit-vscode
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbootstrapAgentContext.ts
More file actions
139 lines (122 loc) · 5.72 KB
/
bootstrapAgentContext.ts
File metadata and controls
139 lines (122 loc) · 5.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/*!
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
import * as path from 'path'
import * as vscode from 'vscode'
import fs from '../shared/fs/fs'
import { getLogger } from '../shared/logger/logger'
import { telemetry } from '../shared/telemetry/telemetry'
import { agentsFile, contextFile, importStatement, notificationMessage, promptMessage } from './shared/constants'
import { extractAccountIdFromResourceMetadata } from './shared/smusUtils'
import { getResourceMetadata } from './shared/utils/resourceMetadataUtils'
import { SmusAuthenticationProvider } from './auth/providers/smusAuthenticationProvider'
function notifyContextUpdated(): void {
void vscode.window.showInformationMessage(notificationMessage)
}
async function promptUserToAddSmusContext(accountId: string, domainId: string | undefined): Promise<boolean> {
const metadata = getResourceMetadata()
const region = metadata?.AdditionalMetadata?.DataZoneDomainRegion
const projectId = metadata?.AdditionalMetadata?.DataZoneProjectId
const spaceKey = metadata?.SpaceName
const authProvider = SmusAuthenticationProvider.fromContext()
// Extract project account ID and region from ResourceArn
// ARN format: arn:aws:sagemaker:region:account-id:space/domain-id/space-name
const arnParts = metadata?.ResourceArn?.split(':')
const projectRegion = arnParts?.[3]
const projectAccountId = arnParts?.[4]
const commonFields = {
smusDomainId: domainId,
smusDomainAccountId: accountId,
smusDomainRegion: region,
smusProjectId: projectId,
smusProjectAccountId: projectAccountId,
smusProjectRegion: projectRegion,
smusSpaceKey: spaceKey,
smusAuthMode: authProvider.activeConnection?.type,
passive: true,
}
telemetry.smus_agentContextShowPrompt.emit({
result: 'Succeeded',
...commonFields,
})
return telemetry.smus_agentContextUserChoice.run(async () => {
const choice = await vscode.window.showWarningMessage(promptMessage, 'Yes', 'No')
if (choice === 'Yes') {
telemetry.record({ smusAcceptAgentContextAction: 'accepted', ...commonFields })
} else if (choice === 'No') {
telemetry.record({ smusAcceptAgentContextAction: 'declined', ...commonFields })
} else {
telemetry.record({ smusAcceptAgentContextAction: 'dismissed', ...commonFields })
}
return choice === 'Yes'
})
}
/**
* Creates or updates ~/smus-context.md with SageMaker Unified Studio context,
* and ensures ~/AGENTS.md imports it.
*
* Behavior:
* - If AGENTS.md doesn't exist, prompts the user. If accepted, creates both files.
* - If AGENTS.md exists with the import, silently updates smus-context.md.
* - If AGENTS.md exists without the import, and smus-context.md already exists,
* silently updates smus-context.md (user removed the import, respect that).
* - If AGENTS.md exists without the import, and smus-context.md doesn't exist,
* prompts the user. If accepted, creates smus-context.md and adds the import.
* If declined, does nothing.
*
* Failures are logged but do not throw.
*/
export async function createAgentsFile(ctx: vscode.ExtensionContext): Promise<void> {
const logger = getLogger('smus')
try {
const templatePath = ctx.asAbsolutePath(path.join('resources', 'smus-context-template.md'))
const content = await fs.readFileText(templatePath)
const contextFileExists = await fs.existsFile(contextFile)
const agentsFileExists = await fs.existsFile(agentsFile)
const accountId = await extractAccountIdFromResourceMetadata()
const metadata = getResourceMetadata()
// Domain ID (DataZone)
const domainId = metadata?.AdditionalMetadata?.DataZoneDomainId
if (!agentsFileExists) {
logger.info('Adding new AGENTS.md file')
if (!(await promptUserToAddSmusContext(accountId, domainId))) {
logger.info('User declined adding SageMaker context')
return
}
await fs.writeFile(contextFile, content)
await fs.writeFile(agentsFile, importStatement + '\n')
logger.info(`Created ${contextFile} and ${agentsFile}`)
notifyContextUpdated()
return
}
const agentsContent = await fs.readFileText(agentsFile)
if (agentsContent.includes(importStatement)) {
logger.info('AGENTS.md contains import for SMUS context')
// Already imported — just update the context file
await fs.writeFile(contextFile, content)
logger.info(`Updated ${contextFile}`)
return
}
if (contextFileExists) {
// smus-context.md exists but isn't imported — user removed it, respect that
// Still update the context file in case they reference it elsewhere
await fs.writeFile(contextFile, content)
logger.info(`Updated ${contextFile}, skipping AGENTS.md (user removed import)`)
notifyContextUpdated()
return
}
// AGENTS.md exists, no import, no smus-context.md — prompt
if (!(await promptUserToAddSmusContext(accountId, domainId))) {
logger.info('User declined adding SageMaker context')
return
}
await fs.writeFile(contextFile, content)
const separator = agentsContent.endsWith('\n') ? '\n' : '\n\n'
await fs.writeFile(agentsFile, agentsContent + separator + importStatement + '\n')
logger.info(`Created ${contextFile} and added import to ${agentsFile}`)
notifyContextUpdated()
} catch (err) {
logger.warn(`Failed to create/update AGENTS.md: ${err}`)
}
}