teamcopilot 0.4.6 → 0.4.8

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.
@@ -24,7 +24,9 @@ const SECRET_PLACEHOLDER_PATTERN = /\{\{SECRET:([A-Za-z_][A-Za-z0-9_]*)\}\}/g
24
24
  const SECRET_ENV_REFERENCE_PATTERN = /\$\{__TEAMCOPILOT_RUNTIME_SECRET_([A-Z][A-Z0-9_]*)\}/g
25
25
  const AGENT_VISIBLE_SECRET_ENV_REFERENCE_PATTERN = /__TEAMCOPILOT_RUNTIME_SECRET_[A-Z][A-Z0-9_]*/
26
26
  const SECRET_ENV_PREFIX = "__TEAMCOPILOT_RUNTIME_SECRET_"
27
- const SHELL_CONTROL_TOKENS = new Set(["&&", "||", ";", "|"])
27
+ const SHELL_CONTROL_TOKENS = new Set(["&&", "||", ";", "|", "\n"])
28
+ const SHELL_REDIRECTION_PATTERN = /^(\d+)?(?:>>?|<<?|<<<)$/
29
+ const SHELL_REDIRECTION_DUPLICATION_PATTERN = /^(\d+)?[<>]&\d+$/
28
30
  const CURL_SAFE_VALUE_OPTIONS = new Set([
29
31
  "-H",
30
32
  "--header",
@@ -160,6 +162,45 @@ type CommandToken = {
160
162
  end: number
161
163
  }
162
164
 
165
+ function readShellRedirectionToken(command: string, start: number): string | null {
166
+ let index = start
167
+ while (/\d/.test(command[index] ?? "")) {
168
+ index += 1
169
+ }
170
+
171
+ if (command[index] !== ">" && command[index] !== "<") {
172
+ if (command[start] !== "&" || command[start + 1] !== ">") {
173
+ return null
174
+ }
175
+ index = start + 2
176
+ } else {
177
+ const operator = command[index]
178
+ index += 1
179
+ while (command[index] === operator && index - start < 4) {
180
+ index += 1
181
+ }
182
+ }
183
+
184
+ if (command[index] === "&") {
185
+ index += 1
186
+ while (/\d/.test(command[index] ?? "")) {
187
+ index += 1
188
+ }
189
+ }
190
+
191
+ return command.slice(start, index)
192
+ }
193
+
194
+ function isEscapedLineContinuation(command: string, newlineIndex: number): boolean {
195
+ let backslashCount = 0
196
+ let index = newlineIndex - 1
197
+ while (index >= 0 && command[index] === "\\") {
198
+ backslashCount += 1
199
+ index -= 1
200
+ }
201
+ return backslashCount % 2 === 1
202
+ }
203
+
163
204
  function tokenizeCommand(command: string): CommandToken[] {
164
205
  const tokens: CommandToken[] = []
165
206
  const length = command.length
@@ -172,6 +213,9 @@ function tokenizeCommand(command: string): CommandToken[] {
172
213
  }
173
214
 
174
215
  if (/\s/.test(char)) {
216
+ if (char === "\n" && !isEscapedLineContinuation(command, index)) {
217
+ tokens.push({ raw: "\n", start: index, end: index + 1 })
218
+ }
175
219
  index += 1
176
220
  continue
177
221
  }
@@ -195,6 +239,13 @@ function tokenizeCommand(command: string): CommandToken[] {
195
239
  continue
196
240
  }
197
241
 
242
+ const redirectionToken = readShellRedirectionToken(command, start)
243
+ if (redirectionToken !== null) {
244
+ index += redirectionToken.length
245
+ tokens.push({ raw: redirectionToken, start, end: index })
246
+ continue
247
+ }
248
+
198
249
  while (index < length) {
199
250
  const current = command[index]
200
251
  if (current === undefined) {
@@ -243,6 +294,10 @@ function tokenizeCommand(command: string): CommandToken[] {
243
294
  break
244
295
  }
245
296
 
297
+ if (current === ">" || current === "<") {
298
+ break
299
+ }
300
+
246
301
  index += 1
247
302
  }
248
303
 
@@ -330,6 +385,16 @@ function isGitExecutableToken(rawToken: string): boolean {
330
385
  return base === "git"
331
386
  }
332
387
 
388
+ function isShellRedirectionToken(rawToken: string): boolean {
389
+ const { inner } = unwrapToken(rawToken)
390
+ return SHELL_REDIRECTION_PATTERN.test(inner) || SHELL_REDIRECTION_DUPLICATION_PATTERN.test(inner) || inner === "&>"
391
+ }
392
+
393
+ function redirectionConsumesNextToken(rawToken: string): boolean {
394
+ const { inner } = unwrapToken(rawToken)
395
+ return !SHELL_REDIRECTION_DUPLICATION_PATTERN.test(inner)
396
+ }
397
+
333
398
  function getLongOptionName(inner: string): string | null {
334
399
  const eqIndex = inner.indexOf("=")
335
400
  const optionName = eqIndex === -1 ? inner : inner.slice(0, eqIndex)
@@ -701,6 +766,13 @@ export const SecretProxyPlugin: Plugin = async ({ client }) => {
701
766
  break
702
767
  }
703
768
 
769
+ if (isShellRedirectionToken(segmentToken.raw)) {
770
+ if (redirectionConsumesNextToken(segmentToken.raw)) {
771
+ j += 1
772
+ }
773
+ continue
774
+ }
775
+
704
776
  const { inner } = unwrapToken(segmentToken.raw)
705
777
 
706
778
  if (expectedValueKind !== null) {
@@ -834,6 +906,13 @@ export const SecretProxyPlugin: Plugin = async ({ client }) => {
834
906
  break
835
907
  }
836
908
 
909
+ if (isShellRedirectionToken(segmentToken.raw)) {
910
+ if (redirectionConsumesNextToken(segmentToken.raw)) {
911
+ j += 1
912
+ }
913
+ continue
914
+ }
915
+
837
916
  const rewritten = rewriteGitRawToken(segmentToken.raw)
838
917
  if (rewritten.rewritten !== segmentToken.raw) {
839
918
  replacements.push({
@@ -879,41 +958,25 @@ export const SecretProxyPlugin: Plugin = async ({ client }) => {
879
958
  assertNoAgentAuthoredSecretEnvReference(input.arguments)
880
959
 
881
960
  const commandCache = new Map<string, { rewritten: string; referencedKeys: string[] }>()
882
- if (typeof input.command === "string" && input.command.includes("{{SECRET:")) {
883
- if ((isCurlExecutableToken(input.command) || isGitExecutableToken(input.command)) && typeof input.arguments === "string") {
884
- const fullCommand = input.arguments.trim().length > 0
885
- ? `${input.command} ${input.arguments}`
886
- : input.command
887
- const rewritten = isCurlExecutableToken(input.command)
888
- ? substitutePlaceholdersInCurlShellString(fullCommand)
889
- : substitutePlaceholdersInGitShellString(fullCommand)
890
- const prefix = `${input.command} `
891
- if (rewritten.rewritten.startsWith(prefix)) {
892
- input.arguments = rewritten.rewritten.slice(prefix.length)
893
- } else {
894
- input.command = rewritten.rewritten
895
- input.arguments = ""
896
- }
961
+ const commandHasPlaceholder = typeof input.command === "string" && input.command.includes("{{SECRET:")
962
+ const argumentsHasPlaceholder = typeof input.arguments === "string" && input.arguments.includes("{{SECRET:")
963
+ if (commandHasPlaceholder || argumentsHasPlaceholder) {
964
+ const originalCommand = typeof input.command === "string" ? input.command : ""
965
+ const originalArguments = typeof input.arguments === "string" ? input.arguments : ""
966
+ const fullCommand = originalArguments.trim().length > 0
967
+ ? `${originalCommand} ${originalArguments}`
968
+ : originalCommand
969
+ const rewritten = await maybeRewriteSupportedString(fullCommand, commandCache)
970
+ const prefix = `${originalCommand} `
971
+
972
+ if (rewritten.rewritten === fullCommand) {
973
+ input.command = originalCommand
974
+ input.arguments = originalArguments
975
+ } else if ((isCurlExecutableToken(originalCommand) || isGitExecutableToken(originalCommand)) && rewritten.rewritten.startsWith(prefix)) {
976
+ input.arguments = rewritten.rewritten.slice(prefix.length)
897
977
  } else {
898
- const rewritten = await maybeRewriteSupportedString(input.command, commandCache)
899
978
  input.command = rewritten.rewritten
900
- }
901
- }
902
- if (typeof input.arguments === "string" && input.arguments.includes("{{SECRET:")) {
903
- if (isCurlExecutableToken(input.command) || isGitExecutableToken(input.command)) {
904
- const fullCommand = input.arguments.trim().length > 0
905
- ? `${input.command} ${input.arguments}`
906
- : input.command
907
- const rewritten = isCurlExecutableToken(input.command)
908
- ? substitutePlaceholdersInCurlShellString(fullCommand)
909
- : substitutePlaceholdersInGitShellString(fullCommand)
910
- const prefix = `${input.command} `
911
- if (rewritten.rewritten.startsWith(prefix)) {
912
- input.arguments = rewritten.rewritten.slice(prefix.length)
913
- }
914
- } else {
915
- const rewritten = await maybeRewriteSupportedString(input.arguments, commandCache)
916
- input.arguments = rewritten.rewritten
979
+ input.arguments = ""
917
980
  }
918
981
  }
919
982
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "teamcopilot",
3
- "version": "0.4.6",
3
+ "version": "0.4.8",
4
4
  "description": "A shared AI Agent for Teams",
5
5
  "homepage": "https://teamcopilot.ai",
6
6
  "repository": {