@forge/cli-shared 8.23.0-next.8-experimental-2e302e1 → 8.23.0

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.
@@ -1,6 +1,16 @@
1
1
  export declare class ForgeCliAttribution {
2
2
  static isAgent(): boolean;
3
3
  static isCI(): boolean;
4
- static build(): Record<string, unknown>;
4
+ static skillName(): string;
5
+ static agentHint(): Promise<string>;
6
+ static isStudio(): boolean;
7
+ static build(): Promise<Record<string, unknown>>;
8
+ private static getWildcardAttribution;
9
+ private static getValidAttributionEnvValue;
10
+ private static isValidAttributionValue;
11
+ private static isValidWildcardKey;
12
+ private static hasEnvKeySet;
13
+ private static camelCaseEnvKey;
14
+ private static capitalize;
5
15
  }
6
16
  //# sourceMappingURL=forge-cli-attribution.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"forge-cli-attribution.d.ts","sourceRoot":"","sources":["../../src/shared/forge-cli-attribution.ts"],"names":[],"mappings":"AAiBA,qBAAa,mBAAmB;WAOhB,OAAO,IAAI,OAAO;WAQlB,IAAI,IAAI,OAAO;WAWf,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAM/C"}
1
+ {"version":3,"file":"forge-cli-attribution.d.ts","sourceRoot":"","sources":["../../src/shared/forge-cli-attribution.ts"],"names":[],"mappings":"AA0CA,qBAAa,mBAAmB;WAOhB,OAAO,IAAI,OAAO;WAQlB,IAAI,IAAI,OAAO;WAIf,SAAS,IAAI,MAAM;WAIb,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC;WAuBlC,QAAQ,IAAI,OAAO;WAWb,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAW7D,OAAO,CAAC,MAAM,CAAC,sBAAsB;IAgDrC,OAAO,CAAC,MAAM,CAAC,2BAA2B;IAK1C,OAAO,CAAC,MAAM,CAAC,uBAAuB;IAStC,OAAO,CAAC,MAAM,CAAC,kBAAkB;IAIjC,OAAO,CAAC,MAAM,CAAC,YAAY;IAK3B,OAAO,CAAC,MAAM,CAAC,eAAe;IAS9B,OAAO,CAAC,MAAM,CAAC,UAAU;CAG1B"}
@@ -1,6 +1,24 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ForgeCliAttribution = void 0;
4
+ const detect_agent_1 = require("@vercel/detect-agent");
5
+ const ATL_FORGE_ATTRIBUTION_ENV_PREFIX = 'ATL_FORGE_ATTRIBUTION_';
6
+ const UNKNOWN = 'unknown';
7
+ const MAX_ATTRIBUTION_VALUE_LENGTH = 512;
8
+ const MAX_WILDCARD_PAYLOAD_BYTES = 4096;
9
+ const MAX_WILDCARD_FIELDS = 32;
10
+ const ATTRIBUTION_VALUE_PATTERN = /^[A-Za-z0-9._\-/:+]+$/;
11
+ const WILDCARD_KEY_PATTERN = /^[A-Za-z][A-Za-z0-9_]*$/;
12
+ const WELL_KNOWN_ATTRIBUTION_ENV_KEYS = new Set(['AGENT_HINT', 'IS_STUDIO', 'SKILL_NAME']);
13
+ const RESERVED_ATTRIBUTION_FIELDS = new Set([
14
+ 'agentHint',
15
+ 'businessUnit',
16
+ 'isAgent',
17
+ 'isCI',
18
+ 'isStudio',
19
+ 'service',
20
+ 'skillName'
21
+ ]);
4
22
  class ForgeCliAttribution {
5
23
  static isAgent() {
6
24
  return !process.stdin.isTTY && !process.stdout.isTTY;
@@ -8,11 +26,104 @@ class ForgeCliAttribution {
8
26
  static isCI() {
9
27
  return process.env.CI === 'true';
10
28
  }
11
- static build() {
29
+ static skillName() {
30
+ return this.getValidAttributionEnvValue('SKILL_NAME') || UNKNOWN;
31
+ }
32
+ static async agentHint() {
33
+ const explicitAgentHint = this.getValidAttributionEnvValue('AGENT_HINT');
34
+ if (explicitAgentHint) {
35
+ return explicitAgentHint;
36
+ }
37
+ if (this.hasEnvKeySet('ROVODEV_CLI')) {
38
+ return 'rovo-dev';
39
+ }
40
+ if (this.hasEnvKeySet('WINDSURF_CASCADE_TERMINAL_KIND')) {
41
+ return 'windsurf';
42
+ }
43
+ try {
44
+ const result = await (0, detect_agent_1.determineAgent)();
45
+ if (result?.isAgent && typeof result.agent?.name === 'string' && result.agent.name) {
46
+ return result.agent.name;
47
+ }
48
+ }
49
+ catch {
50
+ }
51
+ return UNKNOWN;
52
+ }
53
+ static isStudio() {
54
+ return this.getValidAttributionEnvValue('IS_STUDIO') === 'true';
55
+ }
56
+ static async build() {
12
57
  return {
13
58
  isAgent: this.isAgent(),
14
- isCI: this.isCI()
59
+ isCI: this.isCI(),
60
+ skillName: this.skillName(),
61
+ agentHint: await this.agentHint(),
62
+ isStudio: this.isStudio(),
63
+ ...this.getWildcardAttribution()
15
64
  };
16
65
  }
66
+ static getWildcardAttribution() {
67
+ const wildcardAttribution = {};
68
+ let totalBytes = 0;
69
+ const sortedEntries = Object.entries(process.env).sort(([a], [b]) => a.localeCompare(b));
70
+ for (const [envKey, envValue] of sortedEntries) {
71
+ if (!envKey.startsWith(ATL_FORGE_ATTRIBUTION_ENV_PREFIX)) {
72
+ continue;
73
+ }
74
+ const attributionEnvKey = envKey.slice(ATL_FORGE_ATTRIBUTION_ENV_PREFIX.length);
75
+ if (WELL_KNOWN_ATTRIBUTION_ENV_KEYS.has(attributionEnvKey)) {
76
+ continue;
77
+ }
78
+ if (!this.isValidWildcardKey(attributionEnvKey) || !this.isValidAttributionValue(envValue)) {
79
+ continue;
80
+ }
81
+ const attributionKey = this.camelCaseEnvKey(attributionEnvKey);
82
+ if (!attributionKey || RESERVED_ATTRIBUTION_FIELDS.has(attributionKey)) {
83
+ continue;
84
+ }
85
+ if (attributionKey in wildcardAttribution) {
86
+ continue;
87
+ }
88
+ if (Object.keys(wildcardAttribution).length >= MAX_WILDCARD_FIELDS) {
89
+ break;
90
+ }
91
+ const fieldBytes = attributionKey.length + envValue.length + 4;
92
+ if (totalBytes + fieldBytes > MAX_WILDCARD_PAYLOAD_BYTES) {
93
+ continue;
94
+ }
95
+ wildcardAttribution[attributionKey] = envValue;
96
+ totalBytes += fieldBytes;
97
+ }
98
+ return wildcardAttribution;
99
+ }
100
+ static getValidAttributionEnvValue(key) {
101
+ const value = process.env[`${ATL_FORGE_ATTRIBUTION_ENV_PREFIX}${key}`];
102
+ return this.isValidAttributionValue(value) ? value : undefined;
103
+ }
104
+ static isValidAttributionValue(value) {
105
+ return (typeof value === 'string' &&
106
+ value.length > 0 &&
107
+ value.length <= MAX_ATTRIBUTION_VALUE_LENGTH &&
108
+ ATTRIBUTION_VALUE_PATTERN.test(value));
109
+ }
110
+ static isValidWildcardKey(key) {
111
+ return WILDCARD_KEY_PATTERN.test(key);
112
+ }
113
+ static hasEnvKeySet(key) {
114
+ const value = process.env[key];
115
+ return value !== undefined && value !== '';
116
+ }
117
+ static camelCaseEnvKey(key) {
118
+ return key
119
+ .toLowerCase()
120
+ .split('_')
121
+ .filter(Boolean)
122
+ .map((part, index) => (index === 0 ? part : this.capitalize(part)))
123
+ .join('');
124
+ }
125
+ static capitalize(value) {
126
+ return `${value.charAt(0).toUpperCase()}${value.slice(1)}`;
127
+ }
17
128
  }
18
129
  exports.ForgeCliAttribution = ForgeCliAttribution;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forge/cli-shared",
3
- "version": "8.23.0-next.8-experimental-2e302e1",
3
+ "version": "8.23.0",
4
4
  "description": "Common functionality for Forge CLI",
5
5
  "author": "Atlassian",
6
6
  "license": "SEE LICENSE IN LICENSE.txt",
@@ -12,10 +12,11 @@
12
12
  "generate-graphql-types": "graphql-codegen --config src/graphql/codegen.yml"
13
13
  },
14
14
  "dependencies": {
15
- "@forge/manifest": "12.8.0-next.7-experimental-2e302e1",
15
+ "@forge/manifest": "12.8.0",
16
16
  "@forge/util": "2.0.1",
17
17
  "@forge/i18n": "0.0.7",
18
18
  "@sentry/node": "7.106.0",
19
+ "@vercel/detect-agent": "^1.2.3",
19
20
  "adm-zip": "^0.5.10",
20
21
  "array.prototype.flatmap": "^1.3.3",
21
22
  "case": "^1.6.3",
@@ -47,6 +48,7 @@
47
48
  },
48
49
  "devDependencies": {
49
50
  "@atlassian/xen-test-util": "^4.2.0",
51
+ "@colors/colors": "1.5.0",
50
52
  "@graphql-codegen/add": "^5.0.2",
51
53
  "@graphql-codegen/cli": "^5.0.2",
52
54
  "@graphql-codegen/typescript": "^4.1.6",