assistant-ui 0.0.124 → 0.0.126

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.
Files changed (32) hide show
  1. package/README.md +7 -0
  2. package/dist/codemods/v0-12/primitive-if-to-aui-if.d.ts.map +1 -1
  3. package/dist/codemods/v0-12/primitive-if-to-aui-if.js +4 -4
  4. package/dist/codemods/v0-12/primitive-if-to-aui-if.js.map +1 -1
  5. package/dist/commands/cloud.d.ts.map +1 -1
  6. package/dist/commands/cloud.js +12 -3
  7. package/dist/commands/cloud.js.map +1 -1
  8. package/dist/commands/info.d.ts.map +1 -1
  9. package/dist/commands/info.js +8 -1
  10. package/dist/commands/info.js.map +1 -1
  11. package/dist/index.js +0 -0
  12. package/dist/lib/cloud-auth.d.ts +1 -0
  13. package/dist/lib/cloud-auth.d.ts.map +1 -1
  14. package/dist/lib/cloud-auth.js +4 -3
  15. package/dist/lib/cloud-auth.js.map +1 -1
  16. package/dist/lib/cloud-remote.d.ts +22 -0
  17. package/dist/lib/cloud-remote.d.ts.map +1 -0
  18. package/dist/lib/cloud-remote.js +74 -0
  19. package/dist/lib/cloud-remote.js.map +1 -0
  20. package/package.json +7 -7
  21. package/src/codemods/v0-12/__tests__/primitive-if-to-aui-if.test.ts +35 -1
  22. package/src/codemods/v0-12/primitive-if-to-aui-if.ts +5 -4
  23. package/src/commands/cloud.test.ts +58 -0
  24. package/src/commands/cloud.ts +19 -2
  25. package/src/commands/info.ts +26 -1
  26. package/src/lib/cloud-auth.ts +14 -8
  27. package/src/lib/cloud-remote.test.ts +156 -0
  28. package/src/lib/cloud-remote.ts +150 -0
  29. package/templates/cloud-harness/components/assistant-ui/elements/attachment-state.ts +59 -0
  30. package/templates/cloud-harness/components/assistant-ui/elements/attachment.aui.tsx +11 -44
  31. package/templates/cloud-harness/package.json +11 -11
  32. package/templates/cloud-harness/.gitignore +0 -42
package/README.md CHANGED
@@ -103,6 +103,13 @@ setup. Existing environment values are preserved; conflicting cloud settings
103
103
  produce an error. `npx assistant-ui@latest cloud login` signs in separately and
104
104
  `npx assistant-ui@latest cloud logout` revokes the saved CLI login.
105
105
 
106
+ Every other `cloud` subcommand runs on Assistant Cloud: the CLI sends it to the
107
+ cloud management server (`ASSISTANT_UI_CLOUD_URL`, default
108
+ `https://cloud.assistant-ui.com`) with the saved login and prints the answer,
109
+ so new commands arrive without a CLI update. Run
110
+ `npx assistant-ui@latest cloud help` for the current list, such as
111
+ `cloud whoami` and `cloud projects list`.
112
+
106
113
  When an agent is installing from an active setup wizard, pass its setup-agent
107
114
  URL with `--setup-url <url>` to `cloud login` or `cloud setup`. The wizard shows
108
115
  the device approval code and sends you to Accounts for explicit consent, then
@@ -1 +1 @@
1
- {"version":3,"file":"primitive-if-to-aui-if.d.ts","sourceRoot":"","sources":["../../../src/codemods/v0-12/primitive-if-to-aui-if.ts"],"names":[],"mappings":"AA0LA,QAAA,MAAM,yBAAyB,6HAsL9B,CAAC;AAEF,eAAe,yBAAyB,CAAC"}
1
+ {"version":3,"file":"primitive-if-to-aui-if.d.ts","sourceRoot":"","sources":["../../../src/codemods/v0-12/primitive-if-to-aui-if.ts"],"names":[],"mappings":"AA2LA,QAAA,MAAM,yBAAyB,6HAsL9B,CAAC;AAEF,eAAe,yBAAyB,CAAC"}
@@ -29,8 +29,8 @@ const primitiveMap = {
29
29
  expression: `s.message.role ${v === false ? "!==" : "==="} "system"`,
30
30
  negated: false
31
31
  }),
32
- hasBranches: () => ({
33
- expression: "s.message.branchCount >= 2",
32
+ hasBranches: (v) => ({
33
+ expression: v === false ? "true" : "s.message.branchCount >= 2",
34
34
  negated: false
35
35
  }),
36
36
  copied: (v) => ({
@@ -41,8 +41,8 @@ const primitiveMap = {
41
41
  expression: "s.message.isLast",
42
42
  negated: v === false
43
43
  }),
44
- lastOrHover: () => ({
45
- expression: "s.message.isHovering || s.message.isLast",
44
+ lastOrHover: (v) => ({
45
+ expression: v === false ? "true" : "s.message.isHovering || s.message.isLast",
46
46
  negated: false
47
47
  }),
48
48
  speaking: (v) => ({
@@ -1 +1 @@
1
- {"version":3,"file":"primitive-if-to-aui-if.js","names":[],"sources":["../../../src/codemods/v0-12/primitive-if-to-aui-if.ts"],"sourcesContent":["import { createTransformer } from \"../utils/createTransformer\";\nimport { resolveBinding } from \"../utils/resolveBinding\";\n\ntype ConditionFragment = {\n expression: string;\n negated: boolean;\n};\n\n// Map ThreadPrimitive.If props to condition expressions\nconst threadPropMap: Record<\n string,\n (value: unknown) => ConditionFragment | null\n> = {\n empty: (v) => ({\n expression: \"s.thread.isEmpty\",\n negated: v === false,\n }),\n running: (v) => ({\n expression: \"s.thread.isRunning\",\n negated: v === false,\n }),\n disabled: (v) => ({\n expression: \"s.thread.isDisabled\",\n negated: v === false,\n }),\n};\n\n// Map MessagePrimitive.If props to condition expressions\nconst messagePropMap: Record<\n string,\n (value: unknown) => ConditionFragment | null\n> = {\n user: (v) => ({\n expression: `s.message.role ${v === false ? \"!==\" : \"===\"} \"user\"`,\n negated: false,\n }),\n assistant: (v) => ({\n expression: `s.message.role ${v === false ? \"!==\" : \"===\"} \"assistant\"`,\n negated: false,\n }),\n system: (v) => ({\n expression: `s.message.role ${v === false ? \"!==\" : \"===\"} \"system\"`,\n negated: false,\n }),\n hasBranches: () => ({\n expression: \"s.message.branchCount >= 2\",\n negated: false,\n }),\n copied: (v) => ({\n expression: \"s.message.isCopied\",\n negated: v === false,\n }),\n last: (v) => ({\n expression: \"s.message.isLast\",\n negated: v === false,\n }),\n lastOrHover: () => ({\n expression: \"s.message.isHovering || s.message.isLast\",\n negated: false,\n }),\n speaking: (v) => ({\n expression: \"s.message.speech != null\",\n negated: v === false,\n }),\n hasAttachments: (v) =>\n v === true\n ? {\n expression:\n 's.message.role === \"user\" && !!s.message.attachments?.length',\n negated: false,\n }\n : {\n expression:\n 's.message.role !== \"user\" || !s.message.attachments?.length',\n negated: false,\n },\n hasContent: (v) => ({\n expression: \"s.message.parts.length > 0\",\n negated: v === false,\n }),\n submittedFeedback: (v) => {\n if (v === null) {\n return {\n expression:\n \"(s.message.metadata.submittedFeedback?.type ?? null) === null\",\n negated: false,\n };\n }\n return {\n expression: `s.message.metadata.submittedFeedback?.type === \"${v}\"`,\n negated: false,\n };\n },\n};\n\n// Map ComposerPrimitive.If props to condition expressions\nconst composerPropMap: Record<\n string,\n (value: unknown) => ConditionFragment | null\n> = {\n editing: (v) => ({\n expression: \"s.composer.isEditing\",\n negated: v === false,\n }),\n dictation: (v) => ({\n expression: \"s.composer.dictation != null\",\n negated: v === false,\n }),\n};\n\nconst primitiveMap: Record<\n string,\n Record<string, (value: unknown) => ConditionFragment | null>\n> = {\n ThreadPrimitive: threadPropMap,\n MessagePrimitive: messagePropMap,\n ComposerPrimitive: composerPropMap,\n};\n\n// Map of XPrimitive.Component → fixed condition (no props needed)\nconst fixedConditionMap: Record<string, Record<string, string>> = {\n ThreadPrimitive: {\n Empty: \"s.thread.isEmpty\",\n },\n};\n\n// A prop value the maps cannot faithfully express as a static condition\n// (dynamic expressions, `{undefined}`); elements carrying one are skipped\n// so runtime behavior is never silently changed.\nconst UNSUPPORTED_VALUE: unique symbol = Symbol(\"unsupported\");\n\n/**\n * Extract the value of a JSX attribute.\n * - Boolean prop (no value): `<X.If user>` → `true`\n * - `{true}` / `{false}`: → `true` / `false`\n * - `{\"positive\"}`: → `\"positive\"`\n * - `{null}`: → `null`\n * - anything else (dynamic expressions): → UNSUPPORTED_VALUE\n */\nconst getAttrValue = (j: any, attr: any): unknown => {\n // Boolean attribute (no value), e.g. `<X.If user>`\n if (attr.value === null || attr.value === undefined) {\n return true;\n }\n\n // JSX expression container: `{true}`, `{false}`, `{\"positive\"}`, `{null}`\n if (j.JSXExpressionContainer.check(attr.value)) {\n const expr = attr.value.expression;\n if (j.BooleanLiteral.check(expr)) return expr.value;\n // NullLiteral bases Literal in ast-types but carries no `value` field,\n // so it must be recognized before the generic Literal branch.\n if (j.NullLiteral.check(expr)) return null;\n if (j.Literal.check(expr)) {\n if (expr.value === null) return null;\n return expr.value;\n }\n return UNSUPPORTED_VALUE;\n }\n\n // String literal\n if (j.StringLiteral.check(attr.value) || j.Literal.check(attr.value)) {\n return attr.value.value;\n }\n\n return UNSUPPORTED_VALUE;\n};\n\n// Composed as a syntax tree so the printer parenthesizes by precedence;\n// concatenated text lets `!` and `&&` reassociate a fragment's own operators.\nconst buildConditionString = (\n j: any,\n fragments: ConditionFragment[],\n): string => {\n const parseExpression = (source: string) =>\n j(`${source};`).find(j.ExpressionStatement).nodes()[0]!.expression;\n\n const condition = fragments\n .map((f) => {\n const expression = parseExpression(f.expression);\n return f.negated ? j.unaryExpression(\"!\", expression) : expression;\n })\n .reduce((left: any, right: any) => j.logicalExpression(\"&&\", left, right));\n\n return j(condition).toSource();\n};\n\nconst migratePrimitiveIfToAuiIf = createTransformer(\n ({ j, root, markAsChanged }) => {\n const importedPrimitives = new Map<\n string,\n { name: string; declaration: any; binding: any }\n >();\n const auiIfImports: { source: string; binding: any }[] = [];\n const usedNames = new Set<string>();\n root\n .find(j.Identifier)\n .forEach((path: any) => usedNames.add(path.value.name));\n root\n .find(j.JSXIdentifier)\n .forEach((path: any) => usedNames.add(path.value.name));\n\n root.find(j.ImportDeclaration).forEach((path: any) => {\n const declaration = path.value;\n const source = declaration.source.value;\n if (\n typeof source !== \"string\" ||\n !source.startsWith(\"@assistant-ui/\") ||\n declaration.importKind === \"type\"\n )\n return;\n\n declaration.specifiers?.forEach((specifier: any) => {\n if (\n !j.ImportSpecifier.check(specifier) ||\n !j.Identifier.check(specifier.imported) ||\n !j.Identifier.check(specifier.local) ||\n (specifier as { importKind?: string }).importKind === \"type\"\n )\n return;\n const name = specifier.imported.name;\n const binding = specifier.local;\n if (primitiveMap[name] || fixedConditionMap[name]) {\n importedPrimitives.set(binding.name, { name, declaration, binding });\n }\n if (name === \"AuiIf\") auiIfImports.push({ source, binding });\n });\n });\n\n if (importedPrimitives.size === 0) return;\n\n const getPrimitive = (path: any, localName: string) => {\n const primitive = importedPrimitives.get(localName);\n return primitive &&\n resolveBinding(j, path, localName) === primitive.binding\n ? primitive\n : undefined;\n };\n\n const getAuiIfName = (path: any, declaration: any) => {\n const source = declaration.source.value;\n const existing = auiIfImports.find(\n (entry) =>\n entry.source === source &&\n !/^[a-z]/.test(entry.binding.name) &&\n resolveBinding(j, path, entry.binding.name) === entry.binding,\n );\n if (existing) return existing.binding.name;\n\n let name = \"AuiIf\";\n for (let suffix = 1; usedNames.has(name); suffix++)\n name = `AuiIf${suffix}`;\n usedNames.add(name);\n const binding = j.identifier(name);\n declaration.specifiers.push(\n j.importSpecifier(j.identifier(\"AuiIf\"), binding),\n );\n auiIfImports.push({ source, binding });\n return name;\n };\n\n // Opening and closing tags are rewritten together per element, so a\n // skipped element can never be left with a mismatched closing tag.\n const convertElementToAuiIf = (\n elementPath: any,\n conditionBody: string,\n declaration: any,\n ) => {\n const localName = getAuiIfName(elementPath, declaration);\n const arrowFnAst = j(`(s) => ${conditionBody}`)\n .find(j.ArrowFunctionExpression)\n .paths()[0]!.value;\n\n const opening = elementPath.value.openingElement;\n opening.name = j.jsxIdentifier(localName);\n opening.attributes = [\n j.jsxAttribute(\n j.jsxIdentifier(\"condition\"),\n j.jsxExpressionContainer(arrowFnAst),\n ),\n ];\n if (elementPath.value.closingElement) {\n elementPath.value.closingElement.name = j.jsxIdentifier(localName);\n }\n\n markAsChanged();\n };\n\n // Process fixed-condition components: <ThreadPrimitive.Empty> → <AuiIf condition={...}>\n root.find(j.JSXElement).forEach((path: any) => {\n const name = path.value.openingElement.name;\n if (!j.JSXMemberExpression.check(name)) return;\n if (!j.JSXIdentifier.check(name.object)) return;\n if (!j.JSXIdentifier.check(name.property)) return;\n\n const primitive = getPrimitive(path, name.object.name);\n if (!primitive) return;\n const propertyName = name.property.name as string;\n const conditionBody = fixedConditionMap[primitive.name]?.[propertyName];\n if (!conditionBody) return;\n\n // Only transform if there are no props (other than children, which are implicit)\n const attrs: any[] = path.value.openingElement.attributes || [];\n if (attrs.length > 0) return;\n\n convertElementToAuiIf(path, conditionBody, primitive.declaration);\n });\n\n // Process JSX elements: <ThreadPrimitive.If ...> → <AuiIf condition={...}>\n root.find(j.JSXElement).forEach((path: any) => {\n const name = path.value.openingElement.name;\n\n // Check for `<XPrimitive.If ...>`\n if (!j.JSXMemberExpression.check(name)) return;\n if (!j.JSXIdentifier.check(name.object)) return;\n if (!j.JSXIdentifier.check(name.property)) return;\n if (name.property.name !== \"If\") return;\n\n const primitive = getPrimitive(path, name.object.name);\n if (!primitive) return;\n const propMap = primitiveMap[primitive.name];\n if (!propMap) return;\n\n // Extract props\n const attrs: any[] = path.value.openingElement.attributes || [];\n const fragments: ConditionFragment[] = [];\n let hasUnknownProp = false;\n\n for (const attr of attrs) {\n if (!j.JSXAttribute.check(attr)) {\n // JSX spread attributes — can't migrate\n hasUnknownProp = true;\n continue;\n }\n const propName =\n typeof attr.name.name === \"string\" ? attr.name.name : null;\n if (!propName) {\n // e.g. JSXNamespacedName — not expressible as a condition\n hasUnknownProp = true;\n continue;\n }\n\n const mapper = propMap[propName];\n if (!mapper) {\n hasUnknownProp = true;\n continue;\n }\n\n const value = getAttrValue(j, attr);\n if (value === UNSUPPORTED_VALUE) {\n hasUnknownProp = true;\n continue;\n }\n const fragment = mapper(value);\n if (fragment) {\n fragments.push(fragment);\n }\n }\n\n // If we couldn't map all props, skip this element\n if (hasUnknownProp || fragments.length === 0) return;\n\n convertElementToAuiIf(\n path,\n buildConditionString(j, fragments),\n primitive.declaration,\n );\n });\n },\n);\n\nexport default migratePrimitiveIfToAuiIf;\n"],"mappings":"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"}
1
+ {"version":3,"file":"primitive-if-to-aui-if.js","names":[],"sources":["../../../src/codemods/v0-12/primitive-if-to-aui-if.ts"],"sourcesContent":["import { createTransformer } from \"../utils/createTransformer\";\nimport { resolveBinding } from \"../utils/resolveBinding\";\n\ntype ConditionFragment = {\n expression: string;\n negated: boolean;\n};\n\n// Map ThreadPrimitive.If props to condition expressions\nconst threadPropMap: Record<\n string,\n (value: unknown) => ConditionFragment | null\n> = {\n empty: (v) => ({\n expression: \"s.thread.isEmpty\",\n negated: v === false,\n }),\n running: (v) => ({\n expression: \"s.thread.isRunning\",\n negated: v === false,\n }),\n disabled: (v) => ({\n expression: \"s.thread.isDisabled\",\n negated: v === false,\n }),\n};\n\n// Map MessagePrimitive.If props to condition expressions\nconst messagePropMap: Record<\n string,\n (value: unknown) => ConditionFragment | null\n> = {\n user: (v) => ({\n expression: `s.message.role ${v === false ? \"!==\" : \"===\"} \"user\"`,\n negated: false,\n }),\n assistant: (v) => ({\n expression: `s.message.role ${v === false ? \"!==\" : \"===\"} \"assistant\"`,\n negated: false,\n }),\n system: (v) => ({\n expression: `s.message.role ${v === false ? \"!==\" : \"===\"} \"system\"`,\n negated: false,\n }),\n hasBranches: (v) => ({\n expression: v === false ? \"true\" : \"s.message.branchCount >= 2\",\n negated: false,\n }),\n copied: (v) => ({\n expression: \"s.message.isCopied\",\n negated: v === false,\n }),\n last: (v) => ({\n expression: \"s.message.isLast\",\n negated: v === false,\n }),\n lastOrHover: (v) => ({\n expression:\n v === false ? \"true\" : \"s.message.isHovering || s.message.isLast\",\n negated: false,\n }),\n speaking: (v) => ({\n expression: \"s.message.speech != null\",\n negated: v === false,\n }),\n hasAttachments: (v) =>\n v === true\n ? {\n expression:\n 's.message.role === \"user\" && !!s.message.attachments?.length',\n negated: false,\n }\n : {\n expression:\n 's.message.role !== \"user\" || !s.message.attachments?.length',\n negated: false,\n },\n hasContent: (v) => ({\n expression: \"s.message.parts.length > 0\",\n negated: v === false,\n }),\n submittedFeedback: (v) => {\n if (v === null) {\n return {\n expression:\n \"(s.message.metadata.submittedFeedback?.type ?? null) === null\",\n negated: false,\n };\n }\n return {\n expression: `s.message.metadata.submittedFeedback?.type === \"${v}\"`,\n negated: false,\n };\n },\n};\n\n// Map ComposerPrimitive.If props to condition expressions\nconst composerPropMap: Record<\n string,\n (value: unknown) => ConditionFragment | null\n> = {\n editing: (v) => ({\n expression: \"s.composer.isEditing\",\n negated: v === false,\n }),\n dictation: (v) => ({\n expression: \"s.composer.dictation != null\",\n negated: v === false,\n }),\n};\n\nconst primitiveMap: Record<\n string,\n Record<string, (value: unknown) => ConditionFragment | null>\n> = {\n ThreadPrimitive: threadPropMap,\n MessagePrimitive: messagePropMap,\n ComposerPrimitive: composerPropMap,\n};\n\n// Map of XPrimitive.Component → fixed condition (no props needed)\nconst fixedConditionMap: Record<string, Record<string, string>> = {\n ThreadPrimitive: {\n Empty: \"s.thread.isEmpty\",\n },\n};\n\n// A prop value the maps cannot faithfully express as a static condition\n// (dynamic expressions, `{undefined}`); elements carrying one are skipped\n// so runtime behavior is never silently changed.\nconst UNSUPPORTED_VALUE: unique symbol = Symbol(\"unsupported\");\n\n/**\n * Extract the value of a JSX attribute.\n * - Boolean prop (no value): `<X.If user>` → `true`\n * - `{true}` / `{false}`: → `true` / `false`\n * - `{\"positive\"}`: → `\"positive\"`\n * - `{null}`: → `null`\n * - anything else (dynamic expressions): → UNSUPPORTED_VALUE\n */\nconst getAttrValue = (j: any, attr: any): unknown => {\n // Boolean attribute (no value), e.g. `<X.If user>`\n if (attr.value === null || attr.value === undefined) {\n return true;\n }\n\n // JSX expression container: `{true}`, `{false}`, `{\"positive\"}`, `{null}`\n if (j.JSXExpressionContainer.check(attr.value)) {\n const expr = attr.value.expression;\n if (j.BooleanLiteral.check(expr)) return expr.value;\n // NullLiteral bases Literal in ast-types but carries no `value` field,\n // so it must be recognized before the generic Literal branch.\n if (j.NullLiteral.check(expr)) return null;\n if (j.Literal.check(expr)) {\n if (expr.value === null) return null;\n return expr.value;\n }\n return UNSUPPORTED_VALUE;\n }\n\n // String literal\n if (j.StringLiteral.check(attr.value) || j.Literal.check(attr.value)) {\n return attr.value.value;\n }\n\n return UNSUPPORTED_VALUE;\n};\n\n// Composed as a syntax tree so the printer parenthesizes by precedence;\n// concatenated text lets `!` and `&&` reassociate a fragment's own operators.\nconst buildConditionString = (\n j: any,\n fragments: ConditionFragment[],\n): string => {\n const parseExpression = (source: string) =>\n j(`${source};`).find(j.ExpressionStatement).nodes()[0]!.expression;\n\n const condition = fragments\n .map((f) => {\n const expression = parseExpression(f.expression);\n return f.negated ? j.unaryExpression(\"!\", expression) : expression;\n })\n .reduce((left: any, right: any) => j.logicalExpression(\"&&\", left, right));\n\n return j(condition).toSource();\n};\n\nconst migratePrimitiveIfToAuiIf = createTransformer(\n ({ j, root, markAsChanged }) => {\n const importedPrimitives = new Map<\n string,\n { name: string; declaration: any; binding: any }\n >();\n const auiIfImports: { source: string; binding: any }[] = [];\n const usedNames = new Set<string>();\n root\n .find(j.Identifier)\n .forEach((path: any) => usedNames.add(path.value.name));\n root\n .find(j.JSXIdentifier)\n .forEach((path: any) => usedNames.add(path.value.name));\n\n root.find(j.ImportDeclaration).forEach((path: any) => {\n const declaration = path.value;\n const source = declaration.source.value;\n if (\n typeof source !== \"string\" ||\n !source.startsWith(\"@assistant-ui/\") ||\n declaration.importKind === \"type\"\n )\n return;\n\n declaration.specifiers?.forEach((specifier: any) => {\n if (\n !j.ImportSpecifier.check(specifier) ||\n !j.Identifier.check(specifier.imported) ||\n !j.Identifier.check(specifier.local) ||\n (specifier as { importKind?: string }).importKind === \"type\"\n )\n return;\n const name = specifier.imported.name;\n const binding = specifier.local;\n if (primitiveMap[name] || fixedConditionMap[name]) {\n importedPrimitives.set(binding.name, { name, declaration, binding });\n }\n if (name === \"AuiIf\") auiIfImports.push({ source, binding });\n });\n });\n\n if (importedPrimitives.size === 0) return;\n\n const getPrimitive = (path: any, localName: string) => {\n const primitive = importedPrimitives.get(localName);\n return primitive &&\n resolveBinding(j, path, localName) === primitive.binding\n ? primitive\n : undefined;\n };\n\n const getAuiIfName = (path: any, declaration: any) => {\n const source = declaration.source.value;\n const existing = auiIfImports.find(\n (entry) =>\n entry.source === source &&\n !/^[a-z]/.test(entry.binding.name) &&\n resolveBinding(j, path, entry.binding.name) === entry.binding,\n );\n if (existing) return existing.binding.name;\n\n let name = \"AuiIf\";\n for (let suffix = 1; usedNames.has(name); suffix++)\n name = `AuiIf${suffix}`;\n usedNames.add(name);\n const binding = j.identifier(name);\n declaration.specifiers.push(\n j.importSpecifier(j.identifier(\"AuiIf\"), binding),\n );\n auiIfImports.push({ source, binding });\n return name;\n };\n\n // Opening and closing tags are rewritten together per element, so a\n // skipped element can never be left with a mismatched closing tag.\n const convertElementToAuiIf = (\n elementPath: any,\n conditionBody: string,\n declaration: any,\n ) => {\n const localName = getAuiIfName(elementPath, declaration);\n const arrowFnAst = j(`(s) => ${conditionBody}`)\n .find(j.ArrowFunctionExpression)\n .paths()[0]!.value;\n\n const opening = elementPath.value.openingElement;\n opening.name = j.jsxIdentifier(localName);\n opening.attributes = [\n j.jsxAttribute(\n j.jsxIdentifier(\"condition\"),\n j.jsxExpressionContainer(arrowFnAst),\n ),\n ];\n if (elementPath.value.closingElement) {\n elementPath.value.closingElement.name = j.jsxIdentifier(localName);\n }\n\n markAsChanged();\n };\n\n // Process fixed-condition components: <ThreadPrimitive.Empty> → <AuiIf condition={...}>\n root.find(j.JSXElement).forEach((path: any) => {\n const name = path.value.openingElement.name;\n if (!j.JSXMemberExpression.check(name)) return;\n if (!j.JSXIdentifier.check(name.object)) return;\n if (!j.JSXIdentifier.check(name.property)) return;\n\n const primitive = getPrimitive(path, name.object.name);\n if (!primitive) return;\n const propertyName = name.property.name as string;\n const conditionBody = fixedConditionMap[primitive.name]?.[propertyName];\n if (!conditionBody) return;\n\n // Only transform if there are no props (other than children, which are implicit)\n const attrs: any[] = path.value.openingElement.attributes || [];\n if (attrs.length > 0) return;\n\n convertElementToAuiIf(path, conditionBody, primitive.declaration);\n });\n\n // Process JSX elements: <ThreadPrimitive.If ...> → <AuiIf condition={...}>\n root.find(j.JSXElement).forEach((path: any) => {\n const name = path.value.openingElement.name;\n\n // Check for `<XPrimitive.If ...>`\n if (!j.JSXMemberExpression.check(name)) return;\n if (!j.JSXIdentifier.check(name.object)) return;\n if (!j.JSXIdentifier.check(name.property)) return;\n if (name.property.name !== \"If\") return;\n\n const primitive = getPrimitive(path, name.object.name);\n if (!primitive) return;\n const propMap = primitiveMap[primitive.name];\n if (!propMap) return;\n\n // Extract props\n const attrs: any[] = path.value.openingElement.attributes || [];\n const fragments: ConditionFragment[] = [];\n let hasUnknownProp = false;\n\n for (const attr of attrs) {\n if (!j.JSXAttribute.check(attr)) {\n // JSX spread attributes — can't migrate\n hasUnknownProp = true;\n continue;\n }\n const propName =\n typeof attr.name.name === \"string\" ? attr.name.name : null;\n if (!propName) {\n // e.g. JSXNamespacedName — not expressible as a condition\n hasUnknownProp = true;\n continue;\n }\n\n const mapper = propMap[propName];\n if (!mapper) {\n hasUnknownProp = true;\n continue;\n }\n\n const value = getAttrValue(j, attr);\n if (value === UNSUPPORTED_VALUE) {\n hasUnknownProp = true;\n continue;\n }\n const fragment = mapper(value);\n if (fragment) {\n fragments.push(fragment);\n }\n }\n\n // If we couldn't map all props, skip this element\n if (hasUnknownProp || fragments.length === 0) return;\n\n convertElementToAuiIf(\n path,\n buildConditionString(j, fragments),\n primitive.declaration,\n );\n });\n },\n);\n\nexport default migratePrimitiveIfToAuiIf;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"cloud.d.ts","sourceRoot":"","sources":["../../src/commands/cloud.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAuC5C,KAAK,YAAY,GAAG;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAkLF,eAAO,MAAM,UAAU,GACrB,WAAW,MAAM,EACjB,SAAS,YAAY,KACpB,OAAO,CAAC,IAAI,CAmId,CAAC;AAEF,eAAO,MAAM,KAAK,SAkFf,CAAC"}
1
+ {"version":3,"file":"cloud.d.ts","sourceRoot":"","sources":["../../src/commands/cloud.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAyC5C,KAAK,YAAY,GAAG;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAkLF,eAAO,MAAM,UAAU,GACrB,WAAW,MAAM,EACjB,SAAS,YAAY,KACpB,OAAO,CAAC,IAAI,CAmId,CAAC;AAIF,eAAO,MAAM,KAAK,SA+Ff,CAAC"}
@@ -1,6 +1,7 @@
1
1
  import { logger } from "../lib/utils/logger.js";
2
2
  import { validateCloudUrl } from "../lib/cloud-url.js";
3
- import { cloudAccessToken, cloudAuthConfig, loginToCloud, logoutFromCloud } from "../lib/cloud-auth.js";
3
+ import { cloudAccessToken, cloudAuthConfig, loginToCloud, logoutFromCloud, savedCloudAccessToken } from "../lib/cloud-auth.js";
4
+ import { runCloudCommand } from "../lib/cloud-remote.js";
4
5
  import { mergeCloudEnvironment, readCloudProjectConfig, readOptionalFile, writeCloudProjectConfig } from "../lib/cloud-config.js";
5
6
  import { provisionCloudHarness, resolveProvisionRequest } from "../lib/cloud-provision.js";
6
7
  import { create } from "./create.js";
@@ -171,7 +172,15 @@ const setupCloud = async (directory, options) => {
171
172
  logger.info("Add OPENAI_API_KEY to .env.local, then start the app with your package manager's dev command.");
172
173
  logger.info(backend.protocol === "http:" ? `Open ${backend.origin}/#main in two browser windows to try shared chat. Deploy or expose the app to share it with teammates.` : "Open your deployed app and use \"Share this chat\" to invite teammates to the same conversation.");
173
174
  };
174
- const cloud = new Command().name("cloud").description("sign in and set up a multiplayer Assistant Cloud chat").addCommand(new Command("login").description("sign in to Assistant Cloud using your browser").option("--no-open", "print the device sign-in URL without opening a browser").option("--setup-url <url>", "show device sign-in in an active setup wizard").action(async (options) => {
175
+ const DEFAULT_CLOUD_URL = "https://cloud.assistant-ui.com";
176
+ const cloud = new Command().name("cloud").description("manage Assistant Cloud (run `assistant-ui cloud help`)").helpOption(false).helpCommand(false).argument("[command...]").allowUnknownOption().allowExcessArguments().action(async (args) => {
177
+ process.exitCode = await runCloudCommand(args, {
178
+ cloudUrl: process.env.ASSISTANT_UI_CLOUD_URL ?? DEFAULT_CLOUD_URL,
179
+ accessToken: () => savedCloudAccessToken(cloudAuthConfig()),
180
+ stdout: (text) => process.stdout.write(text),
181
+ stderr: (text) => process.stderr.write(text)
182
+ });
183
+ }).addCommand(new Command("login").description("sign in to Assistant Cloud using your browser").option("--no-open", "print the device sign-in URL without opening a browser").option("--setup-url <url>", "show device sign-in in an active setup wizard").action(async (options) => {
175
184
  await loginToCloud(cloudAuthConfig(), {
176
185
  noOpen: options.open === false,
177
186
  ...options.setupUrl && { setupUrl: options.setupUrl },
@@ -180,7 +189,7 @@ const cloud = new Command().name("cloud").description("sign in and set up a mult
180
189
  })).addCommand(new Command("logout").description("revoke your CLI login and remove saved credentials").action(async () => {
181
190
  await logoutFromCloud(cloudAuthConfig());
182
191
  logger.success("Signed out.");
183
- })).addCommand(new Command("setup").description("create a shared AI chat and provision its cloud harness").argument("[directory]", "new project directory", "multiplayer-chat").option("--org <id-or-slug>", "use an existing organization").option("--new-org <slug>", "create an organization").option("--project <id-or-slug>", "use an existing project").option("--new-project <slug>", "create a project").option("--name <name>", "display name for a new project or organization").option("--harness-name <name>", "name for the multiplayer harness").option("--backend-url <url>", "absolute URL of the app's chat route", "http://localhost:3000/api/chat").option("--api-url <url>", "Assistant Cloud management URL", process.env.ASSISTANT_UI_CLOUD_URL ?? "https://cloud.assistant-ui.com").option("--access-code <code>", "redeem your hackathon harness access code").option("--no-open", "print the device sign-in URL without opening a browser").option("--setup-url <url>", "show device sign-in in an active setup wizard").option("--yes", "skip prompts; select resources with flags").option("--skip-install", "skip installing scaffold dependencies").option("--use-npm", "install with npm").option("--use-pnpm", "install with pnpm").option("--use-yarn", "install with yarn").option("--use-bun", "install with bun").addOption(new Option("--debug-source-root <path>", "local assistant-ui source root").hideHelp()).action(async (directory, options) => {
192
+ })).addCommand(new Command("setup").description("create a shared AI chat and provision its cloud harness").argument("[directory]", "new project directory", "multiplayer-chat").option("--org <id-or-slug>", "use an existing organization").option("--new-org <slug>", "create an organization").option("--project <id-or-slug>", "use an existing project").option("--new-project <slug>", "create a project").option("--name <name>", "display name for a new project or organization").option("--harness-name <name>", "name for the multiplayer harness").option("--backend-url <url>", "absolute URL of the app's chat route", "http://localhost:3000/api/chat").option("--api-url <url>", "Assistant Cloud management URL", process.env.ASSISTANT_UI_CLOUD_URL ?? DEFAULT_CLOUD_URL).option("--access-code <code>", "redeem your hackathon harness access code").option("--no-open", "print the device sign-in URL without opening a browser").option("--setup-url <url>", "show device sign-in in an active setup wizard").option("--yes", "skip prompts; select resources with flags").option("--skip-install", "skip installing scaffold dependencies").option("--use-npm", "install with npm").option("--use-pnpm", "install with pnpm").option("--use-yarn", "install with yarn").option("--use-bun", "install with bun").addOption(new Option("--debug-source-root <path>", "local assistant-ui source root").hideHelp()).action(async (directory, options) => {
184
193
  try {
185
194
  await setupCloud(directory, options);
186
195
  } catch (error) {
@@ -1 +1 @@
1
- {"version":3,"file":"cloud.js","names":[],"sources":["../../src/commands/cloud.ts"],"sourcesContent":["import { Command, Option } from \"commander\";\nimport * as p from \"@clack/prompts\";\nimport {\n createProject,\n fetchOrganizations,\n fetchProjects,\n type AccountsOrganization,\n type AccountsProject,\n} from \"aui-auth/accounts\";\nimport { access, readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport {\n cloudAccessToken,\n cloudAuthConfig,\n loginToCloud,\n logoutFromCloud,\n validateCloudUrl,\n} from \"../lib/cloud-auth\";\nimport {\n readCloudProjectConfig,\n mergeCloudEnvironment,\n readOptionalFile,\n writeCloudProjectConfig,\n} from \"../lib/cloud-config\";\nimport {\n provisionCloudHarness,\n resolveProvisionRequest,\n} from \"../lib/cloud-provision\";\nimport { logger } from \"../lib/utils/logger\";\nimport { create } from \"./create\";\n\nclass SetupCancelled extends Error {}\n\nconst answer = <T>(value: T | symbol): Exclude<T, symbol> => {\n if (p.isCancel(value)) throw new SetupCancelled();\n return value as Exclude<T, symbol>;\n};\n\ntype SetupOptions = {\n org?: string;\n newOrg?: string;\n project?: string;\n newProject?: string;\n name?: string;\n harnessName?: string;\n backendUrl: string;\n apiUrl: string;\n accessCode?: string;\n setupUrl?: string;\n open?: boolean;\n yes?: boolean;\n skipInstall?: boolean;\n useNpm?: boolean;\n usePnpm?: boolean;\n useYarn?: boolean;\n useBun?: boolean;\n debugSourceRoot?: string;\n};\n\nconst selectResource = async <\n T extends { id: string; slug: string; name: string },\n>(\n resources: T[],\n selected: string | undefined,\n kind: string,\n yes: boolean,\n): Promise<T | null> => {\n if (selected) {\n const resource = resources.find(\n ({ id, slug }) => selected === id || selected === slug,\n );\n if (!resource)\n throw new Error(`${kind} \"${selected}\" was not found in your account.`);\n return resource;\n }\n if (resources.length === 0) return null;\n if (resources.length === 1) return resources[0]!;\n if (yes)\n throw new Error(\n `Choose a ${kind.toLowerCase()} with --${kind.toLowerCase()} when using --yes.`,\n );\n const id = answer(\n await p.select({\n message: `Choose a ${kind.toLowerCase()}`,\n options: resources.map((resource) => ({\n value: resource.id,\n label: `${resource.name} (${resource.slug})`,\n })),\n }),\n );\n return resources.find((resource) => resource.id === id)!;\n};\n\nconst projectSlug = (value: string): string =>\n value\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n\nconst organizationForSetup = async (\n issuer: string,\n token: string,\n options: SetupOptions,\n): Promise<AccountsOrganization> => {\n const organizations = await fetchOrganizations({ issuer }, token);\n if (!options.newOrg) {\n const existing = await selectResource(\n organizations,\n options.org,\n \"Org\",\n !!options.yes,\n );\n if (existing) return existing;\n }\n const slug =\n options.newOrg ??\n (options.yes\n ? undefined\n : answer(\n await p.text({\n message: \"Choose a slug for your new organization\",\n validate: (value) =>\n /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(value ?? \"\")\n ? undefined\n : \"Use lowercase letters, numbers, and hyphens.\",\n }),\n ));\n if (!slug)\n throw new Error(\n \"No organizations found. Pass --new-org <slug> to create one.\",\n );\n const existing = organizations.find(\n (organization) => organization.slug === slug,\n );\n if (existing) return existing;\n const response = await fetch(`${issuer}/api/oidc/organizations`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ name: options.name ?? slug, slug }),\n signal: AbortSignal.timeout(30_000),\n redirect: \"error\",\n });\n if (!response.ok)\n throw new Error(`Could not create organization (${response.status}).`);\n return (await response.json()) as AccountsOrganization;\n};\n\nconst projectForSetup = async (\n issuer: string,\n token: string,\n organization: AccountsOrganization,\n directory: string,\n options: SetupOptions,\n): Promise<AccountsProject> => {\n const projects = await fetchProjects({ issuer }, token, {\n id: organization.id,\n });\n if (!options.newProject) {\n const existing = await selectResource(\n projects,\n options.project,\n \"Project\",\n !!options.yes,\n );\n if (existing) return existing;\n }\n const slug = options.newProject ?? projectSlug(path.basename(directory));\n if (!slug)\n throw new Error(\"Choose a project slug with --new-project <slug>.\");\n const created = await createProject(\n { issuer },\n token,\n { id: organization.id },\n {\n name: options.name ?? path.basename(directory),\n slug,\n },\n );\n if (\"reason\" in created) {\n const existing = projects.find((project) => project.slug === slug);\n if (existing) return existing;\n throw new Error(\n `Project slug \"${slug}\" already exists. Select it with --project or choose another slug.`,\n );\n }\n return created;\n};\n\nconst scaffoldCloudProject = async (\n directory: string,\n options: SetupOptions,\n): Promise<void> => {\n try {\n await access(path.join(directory, \"package.json\"));\n const marker = JSON.parse(\n await readFile(\n path.join(directory, \".assistant-ui\", \"template.json\"),\n \"utf8\",\n ),\n ) as { template?: string };\n if (marker.template !== \"cloud-harness\")\n throw new Error(\n \"This directory is not a cloud-harness app. Use a new directory.\",\n );\n return;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\n if (await readOptionalFile(path.join(directory, \"package.json\"))) {\n throw new Error(\n \"This directory already contains an app. Use a new directory for the multiplayer scaffold.\",\n );\n }\n }\n const packageRoot = path.resolve(\n path.dirname(fileURLToPath(import.meta.url)),\n \"../..\",\n );\n const args = [\n directory,\n \"--template\",\n \"cloud-harness\",\n \"--no-skills\",\n \"--debug-source-root\",\n options.debugSourceRoot ?? packageRoot,\n ];\n if (options.skipInstall) args.push(\"--skip-install\");\n for (const flag of [\"useNpm\", \"usePnpm\", \"useYarn\", \"useBun\"] as const) {\n if (options[flag]) args.push(`--use-${flag.slice(3).toLowerCase()}`);\n }\n await create.parseAsync(args, { from: \"user\" });\n};\n\nexport const setupCloud = async (\n directory: string,\n options: SetupOptions,\n): Promise<void> => {\n if (options.org && options.newOrg)\n throw new Error(\"Choose --org or --new-org.\");\n if (options.project && options.newProject)\n throw new Error(\"Choose --project or --new-project.\");\n validateCloudUrl(options.apiUrl);\n const backend = new URL(options.backendUrl);\n if (backend.username || backend.password || backend.search || backend.hash)\n throw new Error(\n \"Backend URLs must not include credentials, queries, or fragments.\",\n );\n if (\n options.newOrg !== undefined &&\n !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(options.newOrg)\n )\n throw new Error(\n \"Organization slugs must use lowercase letters, numbers, and hyphens.\",\n );\n if (\n backend.protocol !== \"https:\" &&\n !(\n backend.protocol === \"http:\" &&\n [\"localhost\", \"127.0.0.1\", \"[::1]\"].includes(backend.hostname)\n )\n ) {\n throw new Error(\n \"Use an HTTPS backend URL or an HTTP localhost URL for development.\",\n );\n }\n const target = path.resolve(directory);\n const previous = await readCloudProjectConfig(target);\n if (previous) {\n if (previous.backendUrl !== backend.href)\n throw new Error(\n \"This harness is configured for another backend URL. Update its backend allowlist in the Cloud dashboard and the backendUrl in .assistant-ui/cloud.json before rerunning setup, or use a new directory.\",\n );\n if (\n (options.project &&\n options.project !== previous.projectId &&\n options.project !== previous.projectSlug) ||\n (options.newProject && options.newProject !== previous.projectSlug) ||\n (options.org &&\n options.org !== previous.organizationId &&\n options.org !== previous.organizationSlug) ||\n (options.newOrg && options.newOrg !== previous.organizationSlug)\n )\n throw new Error(\n \"This directory is already configured for another organization or project. Use a new directory.\",\n );\n const environment = await readOptionalFile(path.join(target, \".env.local\"));\n if (!/^\\s*(?:export\\s+)?ASSISTANT_API_KEY\\s*=\\s*.+/m.test(environment))\n throw new Error(\n \"This project is configured but its .env.local API key is missing. Restore it before running setup again.\",\n );\n const expected = mergeCloudEnvironment(environment, {\n NEXT_PUBLIC_ASSISTANT_BASE_URL: previous.apiOrigin,\n NEXT_PUBLIC_ASSISTANT_HARNESS_URL: previous.harnessOrigin,\n NEXT_PUBLIC_ASSISTANT_WORKSPACE_ID: previous.workspaceId,\n });\n if (expected !== environment)\n throw new Error(\n \"Cloud settings are missing from .env.local. Restore them before running setup again.\",\n );\n logger.success(\n `Already configured: ${previous.projectSlug} / ${previous.harnessId}`,\n );\n return;\n }\n const auth = cloudAuthConfig();\n const token = await cloudAccessToken(auth, {\n noOpen: options.open === false,\n ...(options.setupUrl && { setupUrl: options.setupUrl }),\n print: logger.info,\n });\n const organization = await organizationForSetup(auth.issuer, token, options);\n const project = await projectForSetup(\n auth.issuer,\n token,\n organization,\n target,\n options,\n );\n await scaffoldCloudProject(target, options);\n const accessCode =\n options.accessCode ??\n process.env.ASSISTANT_UI_ACCESS_CODE ??\n (options.yes\n ? undefined\n : answer(\n await p.password({\n message:\n \"Hackathon access code (leave empty if your plan includes harnesses)\",\n }),\n ));\n const request = await resolveProvisionRequest(\n target,\n {\n org_id: organization.id,\n project_id: project.id,\n harness_name: options.harnessName ?? `${project.name} chat`,\n backend_urls: backend.protocol === \"http:\" ? [] : [backend.href],\n allow_localhost: backend.protocol === \"http:\",\n },\n accessCode,\n );\n const result = await provisionCloudHarness(options.apiUrl, token, request);\n await writeCloudProjectConfig(\n target,\n {\n organizationId: organization.id,\n organizationSlug: organization.slug,\n projectId: result.project_id,\n projectSlug: project.slug,\n harnessId: result.harness_id,\n harnessOrigin: result.harness_origin,\n apiOrigin: result.api_origin,\n workspaceId: result.workspace_id,\n backendUrl: backend.href,\n },\n result.api_key,\n );\n logger.success(`Multiplayer chat configured for ${project.name}.`);\n logger.info(`Project: ${target}`);\n logger.info(\n \"Add OPENAI_API_KEY to .env.local, then start the app with your package manager's dev command.\",\n );\n logger.info(\n backend.protocol === \"http:\"\n ? `Open ${backend.origin}/#main in two browser windows to try shared chat. Deploy or expose the app to share it with teammates.`\n : 'Open your deployed app and use \"Share this chat\" to invite teammates to the same conversation.',\n );\n};\n\nexport const cloud = new Command()\n .name(\"cloud\")\n .description(\"sign in and set up a multiplayer Assistant Cloud chat\")\n .addCommand(\n new Command(\"login\")\n .description(\"sign in to Assistant Cloud using your browser\")\n .option(\n \"--no-open\",\n \"print the device sign-in URL without opening a browser\",\n )\n .option(\n \"--setup-url <url>\",\n \"show device sign-in in an active setup wizard\",\n )\n .action(async (options: { open?: boolean; setupUrl?: string }) => {\n await loginToCloud(cloudAuthConfig(), {\n noOpen: options.open === false,\n ...(options.setupUrl && { setupUrl: options.setupUrl }),\n print: logger.info,\n });\n }),\n )\n .addCommand(\n new Command(\"logout\")\n .description(\"revoke your CLI login and remove saved credentials\")\n .action(async () => {\n await logoutFromCloud(cloudAuthConfig());\n logger.success(\"Signed out.\");\n }),\n )\n .addCommand(\n new Command(\"setup\")\n .description(\"create a shared AI chat and provision its cloud harness\")\n .argument(\"[directory]\", \"new project directory\", \"multiplayer-chat\")\n .option(\"--org <id-or-slug>\", \"use an existing organization\")\n .option(\"--new-org <slug>\", \"create an organization\")\n .option(\"--project <id-or-slug>\", \"use an existing project\")\n .option(\"--new-project <slug>\", \"create a project\")\n .option(\"--name <name>\", \"display name for a new project or organization\")\n .option(\"--harness-name <name>\", \"name for the multiplayer harness\")\n .option(\n \"--backend-url <url>\",\n \"absolute URL of the app's chat route\",\n \"http://localhost:3000/api/chat\",\n )\n .option(\n \"--api-url <url>\",\n \"Assistant Cloud management URL\",\n process.env.ASSISTANT_UI_CLOUD_URL ?? \"https://cloud.assistant-ui.com\",\n )\n .option(\n \"--access-code <code>\",\n \"redeem your hackathon harness access code\",\n )\n .option(\n \"--no-open\",\n \"print the device sign-in URL without opening a browser\",\n )\n .option(\n \"--setup-url <url>\",\n \"show device sign-in in an active setup wizard\",\n )\n .option(\"--yes\", \"skip prompts; select resources with flags\")\n .option(\"--skip-install\", \"skip installing scaffold dependencies\")\n .option(\"--use-npm\", \"install with npm\")\n .option(\"--use-pnpm\", \"install with pnpm\")\n .option(\"--use-yarn\", \"install with yarn\")\n .option(\"--use-bun\", \"install with bun\")\n .addOption(\n new Option(\n \"--debug-source-root <path>\",\n \"local assistant-ui source root\",\n ).hideHelp(),\n )\n .action(async (directory: string, options: SetupOptions) => {\n try {\n await setupCloud(directory, options);\n } catch (error) {\n if (!(error instanceof SetupCancelled)) throw error;\n p.cancel(\"Cloud setup cancelled. Rerun the same command to resume.\");\n }\n }),\n );\n"],"mappings":";;;;;;;;;;;;;AAgCA,IAAM,iBAAN,cAA6B,MAAM,CAAC;AAEpC,MAAM,UAAa,UAA0C;CAC3D,IAAI,EAAE,SAAS,KAAK,GAAG,MAAM,IAAI,eAAe;CAChD,OAAO;AACT;AAuBA,MAAM,iBAAiB,OAGrB,WACA,UACA,MACA,QACsB;CACtB,IAAI,UAAU;EACZ,MAAM,WAAW,UAAU,MACxB,EAAE,IAAI,WAAW,aAAa,MAAM,aAAa,IACpD;EACA,IAAI,CAAC,UACH,MAAM,IAAI,MAAM,GAAG,KAAK,IAAI,SAAS,iCAAiC;EACxE,OAAO;CACT;CACA,IAAI,UAAU,WAAW,GAAG,OAAO;CACnC,IAAI,UAAU,WAAW,GAAG,OAAO,UAAU;CAC7C,IAAI,KACF,MAAM,IAAI,MACR,YAAY,KAAK,YAAY,EAAE,UAAU,KAAK,YAAY,EAAE,mBAC9D;CACF,MAAM,KAAK,OACT,MAAM,EAAE,OAAO;EACb,SAAS,YAAY,KAAK,YAAY;EACtC,SAAS,UAAU,KAAK,cAAc;GACpC,OAAO,SAAS;GAChB,OAAO,GAAG,SAAS,KAAK,IAAI,SAAS,KAAK;EAC5C,EAAE;CACJ,CAAC,CACH;CACA,OAAO,UAAU,MAAM,aAAa,SAAS,OAAO,EAAE;AACxD;AAEA,MAAM,eAAe,UACnB,MACG,YAAY,CAAC,CACb,QAAQ,eAAe,GAAG,CAAC,CAC3B,QAAQ,UAAU,EAAE;AAEzB,MAAM,uBAAuB,OAC3B,QACA,OACA,YACkC;CAClC,MAAM,gBAAgB,MAAM,mBAAmB,EAAE,OAAO,GAAG,KAAK;CAChE,IAAI,CAAC,QAAQ,QAAQ;EACnB,MAAM,WAAW,MAAM,eACrB,eACA,QAAQ,KACR,OACA,CAAC,CAAC,QAAQ,GACZ;EACA,IAAI,UAAU,OAAO;CACvB;CACA,MAAM,OACJ,QAAQ,WACP,QAAQ,MACL,KAAA,IACA,OACE,MAAM,EAAE,KAAK;EACX,SAAS;EACT,WAAW,UACT,6BAA6B,KAAK,SAAS,EAAE,IACzC,KAAA,IACA;CACR,CAAC,CACH;CACN,IAAI,CAAC,MACH,MAAM,IAAI,MACR,8DACF;CACF,MAAM,WAAW,cAAc,MAC5B,iBAAiB,aAAa,SAAS,IAC1C;CACA,IAAI,UAAU,OAAO;CACrB,MAAM,WAAW,MAAM,MAAM,GAAG,OAAO,0BAA0B;EAC/D,QAAQ;EACR,SAAS;GACP,eAAe,UAAU;GACzB,gBAAgB;EAClB;EACA,MAAM,KAAK,UAAU;GAAE,MAAM,QAAQ,QAAQ;GAAM;EAAK,CAAC;EACzD,QAAQ,YAAY,QAAQ,GAAM;EAClC,UAAU;CACZ,CAAC;CACD,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MAAM,kCAAkC,SAAS,OAAO,GAAG;CACvE,OAAQ,MAAM,SAAS,KAAK;AAC9B;AAEA,MAAM,kBAAkB,OACtB,QACA,OACA,cACA,WACA,YAC6B;CAC7B,MAAM,WAAW,MAAM,cAAc,EAAE,OAAO,GAAG,OAAO,EACtD,IAAI,aAAa,GACnB,CAAC;CACD,IAAI,CAAC,QAAQ,YAAY;EACvB,MAAM,WAAW,MAAM,eACrB,UACA,QAAQ,SACR,WACA,CAAC,CAAC,QAAQ,GACZ;EACA,IAAI,UAAU,OAAO;CACvB;CACA,MAAM,OAAO,QAAQ,cAAc,YAAY,KAAK,SAAS,SAAS,CAAC;CACvE,IAAI,CAAC,MACH,MAAM,IAAI,MAAM,kDAAkD;CACpE,MAAM,UAAU,MAAM,cACpB,EAAE,OAAO,GACT,OACA,EAAE,IAAI,aAAa,GAAG,GACtB;EACE,MAAM,QAAQ,QAAQ,KAAK,SAAS,SAAS;EAC7C;CACF,CACF;CACA,IAAI,YAAY,SAAS;EACvB,MAAM,WAAW,SAAS,MAAM,YAAY,QAAQ,SAAS,IAAI;EACjE,IAAI,UAAU,OAAO;EACrB,MAAM,IAAI,MACR,iBAAiB,KAAK,mEACxB;CACF;CACA,OAAO;AACT;AAEA,MAAM,uBAAuB,OAC3B,WACA,YACkB;CAClB,IAAI;EACF,MAAM,OAAO,KAAK,KAAK,WAAW,cAAc,CAAC;EAOjD,IANe,KAAK,MAClB,MAAM,SACJ,KAAK,KAAK,WAAW,iBAAiB,eAAe,GACrD,MACF,CAEO,CAAC,CAAC,aAAa,iBACtB,MAAM,IAAI,MACR,iEACF;EACF;CACF,SAAS,OAAO;EACd,IAAK,MAAgC,SAAS,UAAU,MAAM;EAC9D,IAAI,MAAM,iBAAiB,KAAK,KAAK,WAAW,cAAc,CAAC,GAC7D,MAAM,IAAI,MACR,2FACF;CAEJ;CACA,MAAM,cAAc,KAAK,QACvB,KAAK,QAAQ,cAAc,YAAY,GAAG,CAAC,GAC3C,OACF;CACA,MAAM,OAAO;EACX;EACA;EACA;EACA;EACA;EACA,QAAQ,mBAAmB;CAC7B;CACA,IAAI,QAAQ,aAAa,KAAK,KAAK,gBAAgB;CACnD,KAAK,MAAM,QAAQ;EAAC;EAAU;EAAW;EAAW;CAAQ,GAC1D,IAAI,QAAQ,OAAO,KAAK,KAAK,SAAS,KAAK,MAAM,CAAC,CAAC,CAAC,YAAY,GAAG;CAErE,MAAM,OAAO,WAAW,MAAM,EAAE,MAAM,OAAO,CAAC;AAChD;AAEA,MAAa,aAAa,OACxB,WACA,YACkB;CAClB,IAAI,QAAQ,OAAO,QAAQ,QACzB,MAAM,IAAI,MAAM,4BAA4B;CAC9C,IAAI,QAAQ,WAAW,QAAQ,YAC7B,MAAM,IAAI,MAAM,oCAAoC;CACtD,iBAAiB,QAAQ,MAAM;CAC/B,MAAM,UAAU,IAAI,IAAI,QAAQ,UAAU;CAC1C,IAAI,QAAQ,YAAY,QAAQ,YAAY,QAAQ,UAAU,QAAQ,MACpE,MAAM,IAAI,MACR,mEACF;CACF,IACE,QAAQ,WAAW,KAAA,KACnB,CAAC,6BAA6B,KAAK,QAAQ,MAAM,GAEjD,MAAM,IAAI,MACR,sEACF;CACF,IACE,QAAQ,aAAa,YACrB,EACE,QAAQ,aAAa,WACrB;EAAC;EAAa;EAAa;CAAO,CAAC,CAAC,SAAS,QAAQ,QAAQ,IAG/D,MAAM,IAAI,MACR,oEACF;CAEF,MAAM,SAAS,KAAK,QAAQ,SAAS;CACrC,MAAM,WAAW,MAAM,uBAAuB,MAAM;CACpD,IAAI,UAAU;EACZ,IAAI,SAAS,eAAe,QAAQ,MAClC,MAAM,IAAI,MACR,wMACF;EACF,IACG,QAAQ,WACP,QAAQ,YAAY,SAAS,aAC7B,QAAQ,YAAY,SAAS,eAC9B,QAAQ,cAAc,QAAQ,eAAe,SAAS,eACtD,QAAQ,OACP,QAAQ,QAAQ,SAAS,kBACzB,QAAQ,QAAQ,SAAS,oBAC1B,QAAQ,UAAU,QAAQ,WAAW,SAAS,kBAE/C,MAAM,IAAI,MACR,gGACF;EACF,MAAM,cAAc,MAAM,iBAAiB,KAAK,KAAK,QAAQ,YAAY,CAAC;EAC1E,IAAI,CAAC,gDAAgD,KAAK,WAAW,GACnE,MAAM,IAAI,MACR,0GACF;EAMF,IALiB,sBAAsB,aAAa;GAClD,gCAAgC,SAAS;GACzC,mCAAmC,SAAS;GAC5C,oCAAoC,SAAS;EAC/C,CACW,MAAM,aACf,MAAM,IAAI,MACR,sFACF;EACF,OAAO,QACL,uBAAuB,SAAS,YAAY,KAAK,SAAS,WAC5D;EACA;CACF;CACA,MAAM,OAAO,gBAAgB;CAC7B,MAAM,QAAQ,MAAM,iBAAiB,MAAM;EACzC,QAAQ,QAAQ,SAAS;EACzB,GAAI,QAAQ,YAAY,EAAE,UAAU,QAAQ,SAAS;EACrD,OAAO,OAAO;CAChB,CAAC;CACD,MAAM,eAAe,MAAM,qBAAqB,KAAK,QAAQ,OAAO,OAAO;CAC3E,MAAM,UAAU,MAAM,gBACpB,KAAK,QACL,OACA,cACA,QACA,OACF;CACA,MAAM,qBAAqB,QAAQ,OAAO;CAC1C,MAAM,aACJ,QAAQ,cACR,QAAQ,IAAI,6BACX,QAAQ,MACL,KAAA,IACA,OACE,MAAM,EAAE,SAAS,EACf,SACE,sEACJ,CAAC,CACH;CACN,MAAM,UAAU,MAAM,wBACpB,QACA;EACE,QAAQ,aAAa;EACrB,YAAY,QAAQ;EACpB,cAAc,QAAQ,eAAe,GAAG,QAAQ,KAAK;EACrD,cAAc,QAAQ,aAAa,UAAU,CAAC,IAAI,CAAC,QAAQ,IAAI;EAC/D,iBAAiB,QAAQ,aAAa;CACxC,GACA,UACF;CACA,MAAM,SAAS,MAAM,sBAAsB,QAAQ,QAAQ,OAAO,OAAO;CACzE,MAAM,wBACJ,QACA;EACE,gBAAgB,aAAa;EAC7B,kBAAkB,aAAa;EAC/B,WAAW,OAAO;EAClB,aAAa,QAAQ;EACrB,WAAW,OAAO;EAClB,eAAe,OAAO;EACtB,WAAW,OAAO;EAClB,aAAa,OAAO;EACpB,YAAY,QAAQ;CACtB,GACA,OAAO,OACT;CACA,OAAO,QAAQ,mCAAmC,QAAQ,KAAK,EAAE;CACjE,OAAO,KAAK,YAAY,QAAQ;CAChC,OAAO,KACL,+FACF;CACA,OAAO,KACL,QAAQ,aAAa,UACjB,QAAQ,QAAQ,OAAO,0GACvB,kGACN;AACF;AAEA,MAAa,QAAQ,IAAI,QAAQ,CAAC,CAC/B,KAAK,OAAO,CAAC,CACb,YAAY,uDAAuD,CAAC,CACpE,WACC,IAAI,QAAQ,OAAO,CAAC,CACjB,YAAY,+CAA+C,CAAC,CAC5D,OACC,aACA,wDACF,CAAC,CACA,OACC,qBACA,+CACF,CAAC,CACA,OAAO,OAAO,YAAmD;CAChE,MAAM,aAAa,gBAAgB,GAAG;EACpC,QAAQ,QAAQ,SAAS;EACzB,GAAI,QAAQ,YAAY,EAAE,UAAU,QAAQ,SAAS;EACrD,OAAO,OAAO;CAChB,CAAC;AACH,CAAC,CACL,CAAC,CACA,WACC,IAAI,QAAQ,QAAQ,CAAC,CAClB,YAAY,oDAAoD,CAAC,CACjE,OAAO,YAAY;CAClB,MAAM,gBAAgB,gBAAgB,CAAC;CACvC,OAAO,QAAQ,aAAa;AAC9B,CAAC,CACL,CAAC,CACA,WACC,IAAI,QAAQ,OAAO,CAAC,CACjB,YAAY,yDAAyD,CAAC,CACtE,SAAS,eAAe,yBAAyB,kBAAkB,CAAC,CACpE,OAAO,sBAAsB,8BAA8B,CAAC,CAC5D,OAAO,oBAAoB,wBAAwB,CAAC,CACpD,OAAO,0BAA0B,yBAAyB,CAAC,CAC3D,OAAO,wBAAwB,kBAAkB,CAAC,CAClD,OAAO,iBAAiB,gDAAgD,CAAC,CACzE,OAAO,yBAAyB,kCAAkC,CAAC,CACnE,OACC,uBACA,wCACA,gCACF,CAAC,CACA,OACC,mBACA,kCACA,QAAQ,IAAI,0BAA0B,gCACxC,CAAC,CACA,OACC,wBACA,2CACF,CAAC,CACA,OACC,aACA,wDACF,CAAC,CACA,OACC,qBACA,+CACF,CAAC,CACA,OAAO,SAAS,2CAA2C,CAAC,CAC5D,OAAO,kBAAkB,uCAAuC,CAAC,CACjE,OAAO,aAAa,kBAAkB,CAAC,CACvC,OAAO,cAAc,mBAAmB,CAAC,CACzC,OAAO,cAAc,mBAAmB,CAAC,CACzC,OAAO,aAAa,kBAAkB,CAAC,CACvC,UACC,IAAI,OACF,8BACA,gCACF,CAAC,CAAC,SAAS,CACb,CAAC,CACA,OAAO,OAAO,WAAmB,YAA0B;CAC1D,IAAI;EACF,MAAM,WAAW,WAAW,OAAO;CACrC,SAAS,OAAO;EACd,IAAI,EAAE,iBAAiB,iBAAiB,MAAM;EAC9C,EAAE,OAAO,0DAA0D;CACrE;AACF,CAAC,CACL"}
1
+ {"version":3,"file":"cloud.js","names":[],"sources":["../../src/commands/cloud.ts"],"sourcesContent":["import { Command, Option } from \"commander\";\nimport * as p from \"@clack/prompts\";\nimport {\n createProject,\n fetchOrganizations,\n fetchProjects,\n type AccountsOrganization,\n type AccountsProject,\n} from \"aui-auth/accounts\";\nimport { access, readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport {\n cloudAccessToken,\n cloudAuthConfig,\n loginToCloud,\n logoutFromCloud,\n savedCloudAccessToken,\n validateCloudUrl,\n} from \"../lib/cloud-auth\";\nimport { runCloudCommand } from \"../lib/cloud-remote\";\nimport {\n readCloudProjectConfig,\n mergeCloudEnvironment,\n readOptionalFile,\n writeCloudProjectConfig,\n} from \"../lib/cloud-config\";\nimport {\n provisionCloudHarness,\n resolveProvisionRequest,\n} from \"../lib/cloud-provision\";\nimport { logger } from \"../lib/utils/logger\";\nimport { create } from \"./create\";\n\nclass SetupCancelled extends Error {}\n\nconst answer = <T>(value: T | symbol): Exclude<T, symbol> => {\n if (p.isCancel(value)) throw new SetupCancelled();\n return value as Exclude<T, symbol>;\n};\n\ntype SetupOptions = {\n org?: string;\n newOrg?: string;\n project?: string;\n newProject?: string;\n name?: string;\n harnessName?: string;\n backendUrl: string;\n apiUrl: string;\n accessCode?: string;\n setupUrl?: string;\n open?: boolean;\n yes?: boolean;\n skipInstall?: boolean;\n useNpm?: boolean;\n usePnpm?: boolean;\n useYarn?: boolean;\n useBun?: boolean;\n debugSourceRoot?: string;\n};\n\nconst selectResource = async <\n T extends { id: string; slug: string; name: string },\n>(\n resources: T[],\n selected: string | undefined,\n kind: string,\n yes: boolean,\n): Promise<T | null> => {\n if (selected) {\n const resource = resources.find(\n ({ id, slug }) => selected === id || selected === slug,\n );\n if (!resource)\n throw new Error(`${kind} \"${selected}\" was not found in your account.`);\n return resource;\n }\n if (resources.length === 0) return null;\n if (resources.length === 1) return resources[0]!;\n if (yes)\n throw new Error(\n `Choose a ${kind.toLowerCase()} with --${kind.toLowerCase()} when using --yes.`,\n );\n const id = answer(\n await p.select({\n message: `Choose a ${kind.toLowerCase()}`,\n options: resources.map((resource) => ({\n value: resource.id,\n label: `${resource.name} (${resource.slug})`,\n })),\n }),\n );\n return resources.find((resource) => resource.id === id)!;\n};\n\nconst projectSlug = (value: string): string =>\n value\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n\nconst organizationForSetup = async (\n issuer: string,\n token: string,\n options: SetupOptions,\n): Promise<AccountsOrganization> => {\n const organizations = await fetchOrganizations({ issuer }, token);\n if (!options.newOrg) {\n const existing = await selectResource(\n organizations,\n options.org,\n \"Org\",\n !!options.yes,\n );\n if (existing) return existing;\n }\n const slug =\n options.newOrg ??\n (options.yes\n ? undefined\n : answer(\n await p.text({\n message: \"Choose a slug for your new organization\",\n validate: (value) =>\n /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(value ?? \"\")\n ? undefined\n : \"Use lowercase letters, numbers, and hyphens.\",\n }),\n ));\n if (!slug)\n throw new Error(\n \"No organizations found. Pass --new-org <slug> to create one.\",\n );\n const existing = organizations.find(\n (organization) => organization.slug === slug,\n );\n if (existing) return existing;\n const response = await fetch(`${issuer}/api/oidc/organizations`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ name: options.name ?? slug, slug }),\n signal: AbortSignal.timeout(30_000),\n redirect: \"error\",\n });\n if (!response.ok)\n throw new Error(`Could not create organization (${response.status}).`);\n return (await response.json()) as AccountsOrganization;\n};\n\nconst projectForSetup = async (\n issuer: string,\n token: string,\n organization: AccountsOrganization,\n directory: string,\n options: SetupOptions,\n): Promise<AccountsProject> => {\n const projects = await fetchProjects({ issuer }, token, {\n id: organization.id,\n });\n if (!options.newProject) {\n const existing = await selectResource(\n projects,\n options.project,\n \"Project\",\n !!options.yes,\n );\n if (existing) return existing;\n }\n const slug = options.newProject ?? projectSlug(path.basename(directory));\n if (!slug)\n throw new Error(\"Choose a project slug with --new-project <slug>.\");\n const created = await createProject(\n { issuer },\n token,\n { id: organization.id },\n {\n name: options.name ?? path.basename(directory),\n slug,\n },\n );\n if (\"reason\" in created) {\n const existing = projects.find((project) => project.slug === slug);\n if (existing) return existing;\n throw new Error(\n `Project slug \"${slug}\" already exists. Select it with --project or choose another slug.`,\n );\n }\n return created;\n};\n\nconst scaffoldCloudProject = async (\n directory: string,\n options: SetupOptions,\n): Promise<void> => {\n try {\n await access(path.join(directory, \"package.json\"));\n const marker = JSON.parse(\n await readFile(\n path.join(directory, \".assistant-ui\", \"template.json\"),\n \"utf8\",\n ),\n ) as { template?: string };\n if (marker.template !== \"cloud-harness\")\n throw new Error(\n \"This directory is not a cloud-harness app. Use a new directory.\",\n );\n return;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\n if (await readOptionalFile(path.join(directory, \"package.json\"))) {\n throw new Error(\n \"This directory already contains an app. Use a new directory for the multiplayer scaffold.\",\n );\n }\n }\n const packageRoot = path.resolve(\n path.dirname(fileURLToPath(import.meta.url)),\n \"../..\",\n );\n const args = [\n directory,\n \"--template\",\n \"cloud-harness\",\n \"--no-skills\",\n \"--debug-source-root\",\n options.debugSourceRoot ?? packageRoot,\n ];\n if (options.skipInstall) args.push(\"--skip-install\");\n for (const flag of [\"useNpm\", \"usePnpm\", \"useYarn\", \"useBun\"] as const) {\n if (options[flag]) args.push(`--use-${flag.slice(3).toLowerCase()}`);\n }\n await create.parseAsync(args, { from: \"user\" });\n};\n\nexport const setupCloud = async (\n directory: string,\n options: SetupOptions,\n): Promise<void> => {\n if (options.org && options.newOrg)\n throw new Error(\"Choose --org or --new-org.\");\n if (options.project && options.newProject)\n throw new Error(\"Choose --project or --new-project.\");\n validateCloudUrl(options.apiUrl);\n const backend = new URL(options.backendUrl);\n if (backend.username || backend.password || backend.search || backend.hash)\n throw new Error(\n \"Backend URLs must not include credentials, queries, or fragments.\",\n );\n if (\n options.newOrg !== undefined &&\n !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(options.newOrg)\n )\n throw new Error(\n \"Organization slugs must use lowercase letters, numbers, and hyphens.\",\n );\n if (\n backend.protocol !== \"https:\" &&\n !(\n backend.protocol === \"http:\" &&\n [\"localhost\", \"127.0.0.1\", \"[::1]\"].includes(backend.hostname)\n )\n ) {\n throw new Error(\n \"Use an HTTPS backend URL or an HTTP localhost URL for development.\",\n );\n }\n const target = path.resolve(directory);\n const previous = await readCloudProjectConfig(target);\n if (previous) {\n if (previous.backendUrl !== backend.href)\n throw new Error(\n \"This harness is configured for another backend URL. Update its backend allowlist in the Cloud dashboard and the backendUrl in .assistant-ui/cloud.json before rerunning setup, or use a new directory.\",\n );\n if (\n (options.project &&\n options.project !== previous.projectId &&\n options.project !== previous.projectSlug) ||\n (options.newProject && options.newProject !== previous.projectSlug) ||\n (options.org &&\n options.org !== previous.organizationId &&\n options.org !== previous.organizationSlug) ||\n (options.newOrg && options.newOrg !== previous.organizationSlug)\n )\n throw new Error(\n \"This directory is already configured for another organization or project. Use a new directory.\",\n );\n const environment = await readOptionalFile(path.join(target, \".env.local\"));\n if (!/^\\s*(?:export\\s+)?ASSISTANT_API_KEY\\s*=\\s*.+/m.test(environment))\n throw new Error(\n \"This project is configured but its .env.local API key is missing. Restore it before running setup again.\",\n );\n const expected = mergeCloudEnvironment(environment, {\n NEXT_PUBLIC_ASSISTANT_BASE_URL: previous.apiOrigin,\n NEXT_PUBLIC_ASSISTANT_HARNESS_URL: previous.harnessOrigin,\n NEXT_PUBLIC_ASSISTANT_WORKSPACE_ID: previous.workspaceId,\n });\n if (expected !== environment)\n throw new Error(\n \"Cloud settings are missing from .env.local. Restore them before running setup again.\",\n );\n logger.success(\n `Already configured: ${previous.projectSlug} / ${previous.harnessId}`,\n );\n return;\n }\n const auth = cloudAuthConfig();\n const token = await cloudAccessToken(auth, {\n noOpen: options.open === false,\n ...(options.setupUrl && { setupUrl: options.setupUrl }),\n print: logger.info,\n });\n const organization = await organizationForSetup(auth.issuer, token, options);\n const project = await projectForSetup(\n auth.issuer,\n token,\n organization,\n target,\n options,\n );\n await scaffoldCloudProject(target, options);\n const accessCode =\n options.accessCode ??\n process.env.ASSISTANT_UI_ACCESS_CODE ??\n (options.yes\n ? undefined\n : answer(\n await p.password({\n message:\n \"Hackathon access code (leave empty if your plan includes harnesses)\",\n }),\n ));\n const request = await resolveProvisionRequest(\n target,\n {\n org_id: organization.id,\n project_id: project.id,\n harness_name: options.harnessName ?? `${project.name} chat`,\n backend_urls: backend.protocol === \"http:\" ? [] : [backend.href],\n allow_localhost: backend.protocol === \"http:\",\n },\n accessCode,\n );\n const result = await provisionCloudHarness(options.apiUrl, token, request);\n await writeCloudProjectConfig(\n target,\n {\n organizationId: organization.id,\n organizationSlug: organization.slug,\n projectId: result.project_id,\n projectSlug: project.slug,\n harnessId: result.harness_id,\n harnessOrigin: result.harness_origin,\n apiOrigin: result.api_origin,\n workspaceId: result.workspace_id,\n backendUrl: backend.href,\n },\n result.api_key,\n );\n logger.success(`Multiplayer chat configured for ${project.name}.`);\n logger.info(`Project: ${target}`);\n logger.info(\n \"Add OPENAI_API_KEY to .env.local, then start the app with your package manager's dev command.\",\n );\n logger.info(\n backend.protocol === \"http:\"\n ? `Open ${backend.origin}/#main in two browser windows to try shared chat. Deploy or expose the app to share it with teammates.`\n : 'Open your deployed app and use \"Share this chat\" to invite teammates to the same conversation.',\n );\n};\n\nconst DEFAULT_CLOUD_URL = \"https://cloud.assistant-ui.com\";\n\nexport const cloud = new Command()\n .name(\"cloud\")\n .description(\"manage Assistant Cloud (run `assistant-ui cloud help`)\")\n .helpOption(false)\n .helpCommand(false)\n .argument(\"[command...]\")\n .allowUnknownOption()\n .allowExcessArguments()\n .action(async (args: string[]) => {\n process.exitCode = await runCloudCommand(args, {\n cloudUrl: process.env.ASSISTANT_UI_CLOUD_URL ?? DEFAULT_CLOUD_URL,\n accessToken: () => savedCloudAccessToken(cloudAuthConfig()),\n stdout: (text) => process.stdout.write(text),\n stderr: (text) => process.stderr.write(text),\n });\n })\n .addCommand(\n new Command(\"login\")\n .description(\"sign in to Assistant Cloud using your browser\")\n .option(\n \"--no-open\",\n \"print the device sign-in URL without opening a browser\",\n )\n .option(\n \"--setup-url <url>\",\n \"show device sign-in in an active setup wizard\",\n )\n .action(async (options: { open?: boolean; setupUrl?: string }) => {\n await loginToCloud(cloudAuthConfig(), {\n noOpen: options.open === false,\n ...(options.setupUrl && { setupUrl: options.setupUrl }),\n print: logger.info,\n });\n }),\n )\n .addCommand(\n new Command(\"logout\")\n .description(\"revoke your CLI login and remove saved credentials\")\n .action(async () => {\n await logoutFromCloud(cloudAuthConfig());\n logger.success(\"Signed out.\");\n }),\n )\n .addCommand(\n new Command(\"setup\")\n .description(\"create a shared AI chat and provision its cloud harness\")\n .argument(\"[directory]\", \"new project directory\", \"multiplayer-chat\")\n .option(\"--org <id-or-slug>\", \"use an existing organization\")\n .option(\"--new-org <slug>\", \"create an organization\")\n .option(\"--project <id-or-slug>\", \"use an existing project\")\n .option(\"--new-project <slug>\", \"create a project\")\n .option(\"--name <name>\", \"display name for a new project or organization\")\n .option(\"--harness-name <name>\", \"name for the multiplayer harness\")\n .option(\n \"--backend-url <url>\",\n \"absolute URL of the app's chat route\",\n \"http://localhost:3000/api/chat\",\n )\n .option(\n \"--api-url <url>\",\n \"Assistant Cloud management URL\",\n process.env.ASSISTANT_UI_CLOUD_URL ?? DEFAULT_CLOUD_URL,\n )\n .option(\n \"--access-code <code>\",\n \"redeem your hackathon harness access code\",\n )\n .option(\n \"--no-open\",\n \"print the device sign-in URL without opening a browser\",\n )\n .option(\n \"--setup-url <url>\",\n \"show device sign-in in an active setup wizard\",\n )\n .option(\"--yes\", \"skip prompts; select resources with flags\")\n .option(\"--skip-install\", \"skip installing scaffold dependencies\")\n .option(\"--use-npm\", \"install with npm\")\n .option(\"--use-pnpm\", \"install with pnpm\")\n .option(\"--use-yarn\", \"install with yarn\")\n .option(\"--use-bun\", \"install with bun\")\n .addOption(\n new Option(\n \"--debug-source-root <path>\",\n \"local assistant-ui source root\",\n ).hideHelp(),\n )\n .action(async (directory: string, options: SetupOptions) => {\n try {\n await setupCloud(directory, options);\n } catch (error) {\n if (!(error instanceof SetupCancelled)) throw error;\n p.cancel(\"Cloud setup cancelled. Rerun the same command to resume.\");\n }\n }),\n );\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"info.d.ts","sourceRoot":"","sources":["../../src/commands/info.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASpC,OAAO,EAAE,iBAAiB,EAAmB,kCAA+B;AAE5E,OAAO,EAAE,iBAAiB,EAAE,CAAC;AAsM7B,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAYtE;AA6LD,eAAO,MAAM,IAAI,SA6Cb,CAAC"}
1
+ {"version":3,"file":"info.d.ts","sourceRoot":"","sources":["../../src/commands/info.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASpC,OAAO,EAAE,iBAAiB,EAAmB,kCAA+B;AAE5E,OAAO,EAAE,iBAAiB,EAAE,CAAC;AA2N7B,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAYtE;AAiMD,eAAO,MAAM,IAAI,SA6Cb,CAAC"}
@@ -31,6 +31,8 @@ const ASSISTANT_UI_PACKAGES = [
31
31
  "@assistant-ui/react-lexical",
32
32
  "@assistant-ui/react-syntax-highlighter",
33
33
  "@assistant-ui/react-hook-form",
34
+ "safe-content-frame",
35
+ "tw-shimmer",
34
36
  "@assistant-ui/react-o11y",
35
37
  "@assistant-ui/react-devtools"
36
38
  ];
@@ -41,6 +43,7 @@ const ECOSYSTEM_PACKAGES = [
41
43
  "next",
42
44
  "vite",
43
45
  "expo",
46
+ "tailwindcss",
44
47
  "ai",
45
48
  "zod",
46
49
  "zustand",
@@ -78,6 +81,10 @@ function getAssistantUiPackageNames(projectPkg) {
78
81
  const declaredPackages = Object.keys(readProjectDeps(projectPkg)).filter((name) => name.startsWith("@assistant-ui/")).sort();
79
82
  return [.../* @__PURE__ */ new Set([...ASSISTANT_UI_PACKAGES, ...declaredPackages])];
80
83
  }
84
+ function getEcosystemPackageNames(packages, cwd) {
85
+ const upstreamPeers = packages.flatMap((pkg) => Object.keys(getPeerDeps(pkg.name, cwd) ?? {})).filter((name) => !name.startsWith("@assistant-ui/") && !ASSISTANT_UI_PACKAGES.includes(name)).sort();
86
+ return [.../* @__PURE__ */ new Set([...ECOSYSTEM_PACKAGES, ...upstreamPeers])];
87
+ }
81
88
  function getSpecifiedRange(pkg, projectPkg) {
82
89
  const deps = projectPkg.dependencies ?? {};
83
90
  const devDeps = projectPkg.devDependencies ?? {};
@@ -175,7 +182,7 @@ function collectWarnings(packages, cwd, projectPkg) {
175
182
  async function collectInfo(cwd, projectPkg) {
176
183
  const pm = await getPackageManagerInfo(cwd);
177
184
  const packages = collectPackages(getAssistantUiPackageNames(projectPkg), cwd, projectPkg);
178
- const ecosystem = collectPackages(ECOSYSTEM_PACKAGES, cwd, projectPkg);
185
+ const ecosystem = collectPackages(getEcosystemPackageNames(packages, cwd), cwd, projectPkg);
179
186
  const warnings = collectWarnings(packages, cwd, projectPkg);
180
187
  return {
181
188
  cliVersion: getCliVersion(),
@@ -1 +1 @@
1
- {"version":3,"file":"info.js","names":["path","fs","os"],"sources":["../../src/commands/info.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport { spawnSync } from \"node:child_process\";\nimport chalk from \"chalk\";\nimport { detect } from \"detect-package-manager\";\nimport { satisfies } from \"semver\";\nimport { logPackageJsonParseError } from \"../lib/utils/package-json\";\nimport { findWorkspaceRoot, resolveRealPath } from \"../lib/utils/workspace\";\n\nexport { findWorkspaceRoot };\n\nconst ASSISTANT_UI_PACKAGES = [\n // Distribution\n \"@assistant-ui/react\",\n \"@assistant-ui/react-native\",\n \"@assistant-ui/react-ink\",\n // Core (should not be installed directly)\n \"@assistant-ui/core\",\n \"@assistant-ui/store\",\n \"@assistant-ui/tap\",\n // Streaming & Cloud\n \"assistant-stream\",\n \"assistant-cloud\",\n // Adapters\n \"@assistant-ui/eve\",\n \"@assistant-ui/ai-sdk\",\n \"@assistant-ui/react-ai-sdk\",\n \"@assistant-ui/react-langgraph\",\n \"@assistant-ui/react-ag-ui\",\n \"@assistant-ui/react-a2a\",\n \"@assistant-ui/react-data-stream\",\n \"@assistant-ui/react-google-adk\",\n // UI / Rendering\n \"@assistant-ui/react-markdown\",\n \"@assistant-ui/react-streamdown\",\n \"@assistant-ui/react-lexical\",\n \"@assistant-ui/react-syntax-highlighter\",\n \"@assistant-ui/react-hook-form\",\n // Observability & DevTools\n \"@assistant-ui/react-o11y\",\n \"@assistant-ui/react-devtools\",\n];\n\nconst ECOSYSTEM_PACKAGES = [\n \"react\",\n \"react-dom\",\n \"react-native\",\n \"next\",\n \"vite\",\n \"expo\",\n \"ai\",\n \"zod\",\n \"zustand\",\n \"typescript\",\n];\n\n// Packages that users should NOT install directly — they are internal\n// dependencies pulled in automatically by distribution packages.\nconst SHOULD_NOT_DIRECT_INSTALL = new Set([\n \"@assistant-ui/core\",\n \"@assistant-ui/store\",\n \"@assistant-ui/tap\",\n]);\n\nfunction resolvePackageJson(pkg: string, cwd: string): string | null {\n let dir = cwd;\n const root = path.parse(dir).root;\n while (dir !== root) {\n const candidate = path.join(\n dir,\n \"node_modules\",\n ...pkg.split(\"/\"),\n \"package.json\",\n );\n if (fs.existsSync(candidate)) return candidate;\n dir = path.dirname(dir);\n }\n return null;\n}\n\nfunction getInstalledVersion(pkg: string, cwd: string): string | null {\n try {\n const pkgJsonPath = resolvePackageJson(pkg, cwd);\n if (pkgJsonPath) {\n const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, \"utf8\"));\n return pkgJson.version ?? null;\n }\n } catch {\n // ignore\n }\n return null;\n}\n\nfunction readProjectDeps(\n projectPkg: Record<string, unknown>,\n): Record<string, string> {\n return {\n ...((projectPkg.dependencies ?? {}) as Record<string, string>),\n ...((projectPkg.devDependencies ?? {}) as Record<string, string>),\n };\n}\n\nfunction getAssistantUiPackageNames(\n projectPkg: Record<string, unknown>,\n): string[] {\n const declaredPackages = Object.keys(readProjectDeps(projectPkg))\n .filter((name) => name.startsWith(\"@assistant-ui/\"))\n .sort();\n\n return [...new Set([...ASSISTANT_UI_PACKAGES, ...declaredPackages])];\n}\n\nfunction getSpecifiedRange(\n pkg: string,\n projectPkg: Record<string, unknown>,\n): string | null {\n const deps = (projectPkg.dependencies ?? {}) as Record<string, string>;\n const devDeps = (projectPkg.devDependencies ?? {}) as Record<string, string>;\n return deps[pkg] ?? devDeps[pkg] ?? null;\n}\n\nfunction getPeerDeps(pkg: string, cwd: string): Record<string, string> | null {\n try {\n const pkgJsonPath = resolvePackageJson(pkg, cwd);\n if (pkgJsonPath) {\n const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, \"utf8\"));\n return (pkgJson.peerDependencies as Record<string, string>) ?? null;\n }\n } catch {\n // ignore\n }\n return null;\n}\n\nfunction detectFramework(\n projectPkg: Record<string, unknown>,\n cwd: string,\n): string {\n const deps = readProjectDeps(projectPkg);\n\n if (deps.next) {\n const v = getInstalledVersion(\"next\", cwd);\n return `Next.js ${v ?? deps.next}`;\n }\n if (deps.expo) {\n const v = getInstalledVersion(\"expo\", cwd);\n return `Expo ${v ?? deps.expo}`;\n }\n if (deps.vite) {\n const v = getInstalledVersion(\"vite\", cwd);\n return `Vite ${v ?? deps.vite}`;\n }\n if (deps[\"@remix-run/react\"] || deps.remix) return \"Remix\";\n if (deps.gatsby) return \"Gatsby\";\n if (deps.astro) return \"Astro\";\n return \"Unknown\";\n}\n\nfunction getOsInfo(): string {\n const platform = os.platform();\n const arch = os.arch();\n const release = os.release();\n\n switch (platform) {\n case \"darwin\": {\n const result = spawnSync(\"sw_vers\", [\"-productVersion\"], {\n encoding: \"utf8\",\n });\n const macVer = result.stdout?.trim();\n return macVer\n ? `macOS ${macVer} (${arch})`\n : `macOS ${release} (${arch})`;\n }\n case \"win32\":\n return `Windows ${release} (${arch})`;\n case \"linux\":\n return `Linux ${release} (${arch})`;\n default:\n return `${platform} ${release} (${arch})`;\n }\n}\n\nfunction getCliVersion(): string {\n try {\n const packageJson = JSON.parse(\n fs.readFileSync(new URL(\"../../package.json\", import.meta.url), \"utf8\"),\n ) as { version?: unknown };\n return typeof packageJson.version === \"string\"\n ? packageJson.version\n : \"unknown\";\n } catch {\n return \"unknown\";\n }\n}\n\nasync function getPackageManagerInfo(\n cwd: string,\n): Promise<{ name: string; version: string }> {\n const pm = await detect({ cwd });\n const result = spawnSync(pm, [\"--version\"], {\n encoding: \"utf8\",\n cwd,\n });\n const version = result.stdout?.trim() ?? \"unknown\";\n return { name: pm, version };\n}\n\nexport function satisfiesRange(version: string, range: string): boolean {\n const normalizedRange = range.startsWith(\"workspace:\")\n ? range.slice(\"workspace:\".length)\n : range;\n\n return (\n normalizedRange === \"\" ||\n normalizedRange === \"^\" ||\n normalizedRange === \"~\" ||\n normalizedRange === \"any\" ||\n satisfies(version, normalizedRange, { includePrerelease: true })\n );\n}\n\ninterface PackageInfo {\n name: string;\n version: string;\n range: string | null;\n}\n\ninterface InfoData {\n cliVersion: string;\n os: string;\n node: string;\n pm: { name: string; version: string };\n framework: string;\n isMonorepo: boolean;\n packages: PackageInfo[];\n ecosystem: PackageInfo[];\n warnings: string[];\n}\n\nfunction collectPackages(\n names: string[],\n cwd: string,\n projectPkg: Record<string, unknown>,\n): PackageInfo[] {\n const result: PackageInfo[] = [];\n const deps = readProjectDeps(projectPkg);\n\n for (const name of names) {\n const version = getInstalledVersion(name, cwd);\n if (version) {\n result.push({\n name,\n version,\n range: getSpecifiedRange(name, projectPkg),\n });\n } else {\n // Fallback: no node_modules, show range from package.json\n const range = deps[name];\n if (range && !range.startsWith(\"workspace:\")) {\n result.push({ name, version: `${range} (not installed)`, range });\n }\n }\n }\n return result;\n}\n\nfunction collectWarnings(\n packages: PackageInfo[],\n cwd: string,\n projectPkg: Record<string, unknown>,\n): string[] {\n const warnings: string[] = [];\n const deps = readProjectDeps(projectPkg);\n\n // Check peer dependency mismatches\n for (const pkg of packages) {\n const peerDeps = getPeerDeps(pkg.name, cwd);\n if (!peerDeps) continue;\n\n for (const [peerName, peerRange] of Object.entries(peerDeps)) {\n const peerVersion = getInstalledVersion(peerName, cwd);\n if (!peerVersion) continue;\n if (!satisfiesRange(peerVersion, peerRange)) {\n warnings.push(\n `${pkg.name} requires ${peerName} ${peerRange}, found ${peerVersion}`,\n );\n }\n }\n }\n\n // Check for direct install of internal packages\n for (const name of SHOULD_NOT_DIRECT_INSTALL) {\n if (deps[name]) {\n warnings.push(\n `${name} should not be installed directly — it is an internal dependency`,\n );\n }\n }\n\n return warnings;\n}\n\nasync function collectInfo(\n cwd: string,\n projectPkg: Record<string, unknown>,\n): Promise<InfoData> {\n const pm = await getPackageManagerInfo(cwd);\n const packages = collectPackages(\n getAssistantUiPackageNames(projectPkg),\n cwd,\n projectPkg,\n );\n const ecosystem = collectPackages(ECOSYSTEM_PACKAGES, cwd, projectPkg);\n const warnings = collectWarnings(packages, cwd, projectPkg);\n\n return {\n cliVersion: getCliVersion(),\n os: getOsInfo(),\n node: process.version,\n pm,\n framework: detectFramework(projectPkg, cwd),\n isMonorepo: findWorkspaceRoot(cwd) !== null,\n packages,\n ecosystem,\n warnings,\n };\n}\n\nfunction formatSection(label: string, items: PackageInfo[]): string[] {\n if (items.length === 0) return [];\n const lines: string[] = [];\n lines.push(\"\");\n lines.push(`${label}:`);\n const maxLen = Math.max(...items.map((p) => p.name.length));\n for (const pkg of items) {\n lines.push(` ${pkg.name.padEnd(maxLen)} ${pkg.version}`);\n }\n return lines;\n}\n\nfunction renderPlain(data: InfoData): string[] {\n const lines: string[] = [];\n\n lines.push(\"Environment:\");\n lines.push(` assistant-ui CLI: ${data.cliVersion}`);\n lines.push(` OS: ${data.os}`);\n lines.push(` Node.js: ${data.node}`);\n lines.push(` Package Manager: ${data.pm.name} ${data.pm.version}`);\n lines.push(` Framework: ${data.framework}`);\n if (data.isMonorepo) {\n lines.push(` Monorepo: yes`);\n }\n\n lines.push(...formatSection(\"Packages\", data.packages));\n lines.push(...formatSection(\"Ecosystem\", data.ecosystem));\n\n if (data.warnings.length > 0) {\n lines.push(\"\");\n lines.push(\"Warnings:\");\n for (const w of data.warnings) {\n lines.push(` ! ${w}`);\n }\n }\n\n return lines;\n}\n\nfunction renderColored(data: InfoData): string[] {\n const lines: string[] = [];\n\n lines.push(chalk.bold(\"Environment:\"));\n lines.push(` assistant-ui CLI: ${data.cliVersion}`);\n lines.push(` OS: ${data.os}`);\n lines.push(` Node.js: ${data.node}`);\n lines.push(` Package Manager: ${data.pm.name} ${data.pm.version}`);\n lines.push(` Framework: ${data.framework}`);\n if (data.isMonorepo) {\n lines.push(` Monorepo: yes`);\n }\n\n if (data.packages.length > 0) {\n const section = formatSection(\"Packages\", data.packages);\n section[0] = \"\";\n section[1] = chalk.bold(\"Packages:\");\n lines.push(...section);\n } else {\n lines.push(\"\");\n lines.push(chalk.yellow(\" No assistant-ui packages found.\"));\n }\n\n if (data.ecosystem.length > 0) {\n const section = formatSection(\"Ecosystem\", data.ecosystem);\n section[0] = \"\";\n section[1] = chalk.bold(\"Ecosystem:\");\n lines.push(...section);\n }\n\n if (data.warnings.length > 0) {\n lines.push(\"\");\n lines.push(chalk.yellow.bold(\"Warnings:\"));\n for (const w of data.warnings) {\n lines.push(chalk.yellow(` ! ${w}`));\n }\n }\n\n return lines;\n}\n\nexport const info = new Command()\n .name(\"info\")\n .description(\"Print environment and package information for bug reports.\")\n .option(\n \"-c, --cwd <cwd>\",\n \"the working directory. defaults to the current directory.\",\n process.cwd(),\n )\n .action(async (opts) => {\n const cwd = resolveRealPath(opts.cwd);\n const packageJsonPath = path.join(cwd, \"package.json\");\n\n if (!fs.existsSync(packageJsonPath)) {\n console.error(\n chalk.red(\"No package.json found in the current directory.\"),\n );\n process.exit(1);\n }\n\n const packageJsonContent = fs.readFileSync(packageJsonPath, \"utf8\");\n let projectPkg: Record<string, unknown>;\n try {\n projectPkg = JSON.parse(packageJsonContent);\n } catch {\n logPackageJsonParseError(packageJsonPath, \"info\");\n process.exit(1);\n }\n\n const data = await collectInfo(cwd, projectPkg);\n\n // Colored output for terminal\n console.log(\"\");\n for (const line of renderColored(data)) {\n console.log(line);\n }\n console.log(\"\");\n\n // Copyable plain text\n const plain = renderPlain(data);\n const block = [\"```\", ...plain, \"```\"].join(\"\\n\");\n\n console.log(chalk.dim(\"— Copy the text below into your bug report —\"));\n console.log(\"\");\n console.log(block);\n console.log(\"\");\n });\n"],"mappings":"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"}
1
+ {"version":3,"file":"info.js","names":["path","fs","os"],"sources":["../../src/commands/info.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport { spawnSync } from \"node:child_process\";\nimport chalk from \"chalk\";\nimport { detect } from \"detect-package-manager\";\nimport { satisfies } from \"semver\";\nimport { logPackageJsonParseError } from \"../lib/utils/package-json\";\nimport { findWorkspaceRoot, resolveRealPath } from \"../lib/utils/workspace\";\n\nexport { findWorkspaceRoot };\n\nconst ASSISTANT_UI_PACKAGES = [\n // Distribution\n \"@assistant-ui/react\",\n \"@assistant-ui/react-native\",\n \"@assistant-ui/react-ink\",\n // Core (should not be installed directly)\n \"@assistant-ui/core\",\n \"@assistant-ui/store\",\n \"@assistant-ui/tap\",\n // Streaming & Cloud\n \"assistant-stream\",\n \"assistant-cloud\",\n // Adapters\n \"@assistant-ui/eve\",\n \"@assistant-ui/ai-sdk\",\n \"@assistant-ui/react-ai-sdk\",\n \"@assistant-ui/react-langgraph\",\n \"@assistant-ui/react-ag-ui\",\n \"@assistant-ui/react-a2a\",\n \"@assistant-ui/react-data-stream\",\n \"@assistant-ui/react-google-adk\",\n // UI / Rendering\n \"@assistant-ui/react-markdown\",\n \"@assistant-ui/react-streamdown\",\n \"@assistant-ui/react-lexical\",\n \"@assistant-ui/react-syntax-highlighter\",\n \"@assistant-ui/react-hook-form\",\n \"safe-content-frame\",\n \"tw-shimmer\",\n // Observability & DevTools\n \"@assistant-ui/react-o11y\",\n \"@assistant-ui/react-devtools\",\n];\n\nconst ECOSYSTEM_PACKAGES = [\n \"react\",\n \"react-dom\",\n \"react-native\",\n \"next\",\n \"vite\",\n \"expo\",\n \"tailwindcss\",\n \"ai\",\n \"zod\",\n \"zustand\",\n \"typescript\",\n];\n\n// Packages that users should NOT install directly — they are internal\n// dependencies pulled in automatically by distribution packages.\nconst SHOULD_NOT_DIRECT_INSTALL = new Set([\n \"@assistant-ui/core\",\n \"@assistant-ui/store\",\n \"@assistant-ui/tap\",\n]);\n\nfunction resolvePackageJson(pkg: string, cwd: string): string | null {\n let dir = cwd;\n const root = path.parse(dir).root;\n while (dir !== root) {\n const candidate = path.join(\n dir,\n \"node_modules\",\n ...pkg.split(\"/\"),\n \"package.json\",\n );\n if (fs.existsSync(candidate)) return candidate;\n dir = path.dirname(dir);\n }\n return null;\n}\n\nfunction getInstalledVersion(pkg: string, cwd: string): string | null {\n try {\n const pkgJsonPath = resolvePackageJson(pkg, cwd);\n if (pkgJsonPath) {\n const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, \"utf8\"));\n return pkgJson.version ?? null;\n }\n } catch {\n // ignore\n }\n return null;\n}\n\nfunction readProjectDeps(\n projectPkg: Record<string, unknown>,\n): Record<string, string> {\n return {\n ...((projectPkg.dependencies ?? {}) as Record<string, string>),\n ...((projectPkg.devDependencies ?? {}) as Record<string, string>),\n };\n}\n\nfunction getAssistantUiPackageNames(\n projectPkg: Record<string, unknown>,\n): string[] {\n const declaredPackages = Object.keys(readProjectDeps(projectPkg))\n .filter((name) => name.startsWith(\"@assistant-ui/\"))\n .sort();\n\n return [...new Set([...ASSISTANT_UI_PACKAGES, ...declaredPackages])];\n}\n\n// Adapters peer on the provider SDK they wrap, so the installed packages\n// name the upstream versions a bug report needs.\nfunction getEcosystemPackageNames(\n packages: PackageInfo[],\n cwd: string,\n): string[] {\n const upstreamPeers = packages\n .flatMap((pkg) => Object.keys(getPeerDeps(pkg.name, cwd) ?? {}))\n .filter(\n (name) =>\n !name.startsWith(\"@assistant-ui/\") &&\n !ASSISTANT_UI_PACKAGES.includes(name),\n )\n .sort();\n\n return [...new Set([...ECOSYSTEM_PACKAGES, ...upstreamPeers])];\n}\n\nfunction getSpecifiedRange(\n pkg: string,\n projectPkg: Record<string, unknown>,\n): string | null {\n const deps = (projectPkg.dependencies ?? {}) as Record<string, string>;\n const devDeps = (projectPkg.devDependencies ?? {}) as Record<string, string>;\n return deps[pkg] ?? devDeps[pkg] ?? null;\n}\n\nfunction getPeerDeps(pkg: string, cwd: string): Record<string, string> | null {\n try {\n const pkgJsonPath = resolvePackageJson(pkg, cwd);\n if (pkgJsonPath) {\n const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, \"utf8\"));\n return (pkgJson.peerDependencies as Record<string, string>) ?? null;\n }\n } catch {\n // ignore\n }\n return null;\n}\n\nfunction detectFramework(\n projectPkg: Record<string, unknown>,\n cwd: string,\n): string {\n const deps = readProjectDeps(projectPkg);\n\n if (deps.next) {\n const v = getInstalledVersion(\"next\", cwd);\n return `Next.js ${v ?? deps.next}`;\n }\n if (deps.expo) {\n const v = getInstalledVersion(\"expo\", cwd);\n return `Expo ${v ?? deps.expo}`;\n }\n if (deps.vite) {\n const v = getInstalledVersion(\"vite\", cwd);\n return `Vite ${v ?? deps.vite}`;\n }\n if (deps[\"@remix-run/react\"] || deps.remix) return \"Remix\";\n if (deps.gatsby) return \"Gatsby\";\n if (deps.astro) return \"Astro\";\n return \"Unknown\";\n}\n\nfunction getOsInfo(): string {\n const platform = os.platform();\n const arch = os.arch();\n const release = os.release();\n\n switch (platform) {\n case \"darwin\": {\n const result = spawnSync(\"sw_vers\", [\"-productVersion\"], {\n encoding: \"utf8\",\n });\n const macVer = result.stdout?.trim();\n return macVer\n ? `macOS ${macVer} (${arch})`\n : `macOS ${release} (${arch})`;\n }\n case \"win32\":\n return `Windows ${release} (${arch})`;\n case \"linux\":\n return `Linux ${release} (${arch})`;\n default:\n return `${platform} ${release} (${arch})`;\n }\n}\n\nfunction getCliVersion(): string {\n try {\n const packageJson = JSON.parse(\n fs.readFileSync(new URL(\"../../package.json\", import.meta.url), \"utf8\"),\n ) as { version?: unknown };\n return typeof packageJson.version === \"string\"\n ? packageJson.version\n : \"unknown\";\n } catch {\n return \"unknown\";\n }\n}\n\nasync function getPackageManagerInfo(\n cwd: string,\n): Promise<{ name: string; version: string }> {\n const pm = await detect({ cwd });\n const result = spawnSync(pm, [\"--version\"], {\n encoding: \"utf8\",\n cwd,\n });\n const version = result.stdout?.trim() ?? \"unknown\";\n return { name: pm, version };\n}\n\nexport function satisfiesRange(version: string, range: string): boolean {\n const normalizedRange = range.startsWith(\"workspace:\")\n ? range.slice(\"workspace:\".length)\n : range;\n\n return (\n normalizedRange === \"\" ||\n normalizedRange === \"^\" ||\n normalizedRange === \"~\" ||\n normalizedRange === \"any\" ||\n satisfies(version, normalizedRange, { includePrerelease: true })\n );\n}\n\ninterface PackageInfo {\n name: string;\n version: string;\n range: string | null;\n}\n\ninterface InfoData {\n cliVersion: string;\n os: string;\n node: string;\n pm: { name: string; version: string };\n framework: string;\n isMonorepo: boolean;\n packages: PackageInfo[];\n ecosystem: PackageInfo[];\n warnings: string[];\n}\n\nfunction collectPackages(\n names: string[],\n cwd: string,\n projectPkg: Record<string, unknown>,\n): PackageInfo[] {\n const result: PackageInfo[] = [];\n const deps = readProjectDeps(projectPkg);\n\n for (const name of names) {\n const version = getInstalledVersion(name, cwd);\n if (version) {\n result.push({\n name,\n version,\n range: getSpecifiedRange(name, projectPkg),\n });\n } else {\n // Fallback: no node_modules, show range from package.json\n const range = deps[name];\n if (range && !range.startsWith(\"workspace:\")) {\n result.push({ name, version: `${range} (not installed)`, range });\n }\n }\n }\n return result;\n}\n\nfunction collectWarnings(\n packages: PackageInfo[],\n cwd: string,\n projectPkg: Record<string, unknown>,\n): string[] {\n const warnings: string[] = [];\n const deps = readProjectDeps(projectPkg);\n\n // Check peer dependency mismatches\n for (const pkg of packages) {\n const peerDeps = getPeerDeps(pkg.name, cwd);\n if (!peerDeps) continue;\n\n for (const [peerName, peerRange] of Object.entries(peerDeps)) {\n const peerVersion = getInstalledVersion(peerName, cwd);\n if (!peerVersion) continue;\n if (!satisfiesRange(peerVersion, peerRange)) {\n warnings.push(\n `${pkg.name} requires ${peerName} ${peerRange}, found ${peerVersion}`,\n );\n }\n }\n }\n\n // Check for direct install of internal packages\n for (const name of SHOULD_NOT_DIRECT_INSTALL) {\n if (deps[name]) {\n warnings.push(\n `${name} should not be installed directly — it is an internal dependency`,\n );\n }\n }\n\n return warnings;\n}\n\nasync function collectInfo(\n cwd: string,\n projectPkg: Record<string, unknown>,\n): Promise<InfoData> {\n const pm = await getPackageManagerInfo(cwd);\n const packages = collectPackages(\n getAssistantUiPackageNames(projectPkg),\n cwd,\n projectPkg,\n );\n const ecosystem = collectPackages(\n getEcosystemPackageNames(packages, cwd),\n cwd,\n projectPkg,\n );\n const warnings = collectWarnings(packages, cwd, projectPkg);\n\n return {\n cliVersion: getCliVersion(),\n os: getOsInfo(),\n node: process.version,\n pm,\n framework: detectFramework(projectPkg, cwd),\n isMonorepo: findWorkspaceRoot(cwd) !== null,\n packages,\n ecosystem,\n warnings,\n };\n}\n\nfunction formatSection(label: string, items: PackageInfo[]): string[] {\n if (items.length === 0) return [];\n const lines: string[] = [];\n lines.push(\"\");\n lines.push(`${label}:`);\n const maxLen = Math.max(...items.map((p) => p.name.length));\n for (const pkg of items) {\n lines.push(` ${pkg.name.padEnd(maxLen)} ${pkg.version}`);\n }\n return lines;\n}\n\nfunction renderPlain(data: InfoData): string[] {\n const lines: string[] = [];\n\n lines.push(\"Environment:\");\n lines.push(` assistant-ui CLI: ${data.cliVersion}`);\n lines.push(` OS: ${data.os}`);\n lines.push(` Node.js: ${data.node}`);\n lines.push(` Package Manager: ${data.pm.name} ${data.pm.version}`);\n lines.push(` Framework: ${data.framework}`);\n if (data.isMonorepo) {\n lines.push(` Monorepo: yes`);\n }\n\n lines.push(...formatSection(\"Packages\", data.packages));\n lines.push(...formatSection(\"Ecosystem\", data.ecosystem));\n\n if (data.warnings.length > 0) {\n lines.push(\"\");\n lines.push(\"Warnings:\");\n for (const w of data.warnings) {\n lines.push(` ! ${w}`);\n }\n }\n\n return lines;\n}\n\nfunction renderColored(data: InfoData): string[] {\n const lines: string[] = [];\n\n lines.push(chalk.bold(\"Environment:\"));\n lines.push(` assistant-ui CLI: ${data.cliVersion}`);\n lines.push(` OS: ${data.os}`);\n lines.push(` Node.js: ${data.node}`);\n lines.push(` Package Manager: ${data.pm.name} ${data.pm.version}`);\n lines.push(` Framework: ${data.framework}`);\n if (data.isMonorepo) {\n lines.push(` Monorepo: yes`);\n }\n\n if (data.packages.length > 0) {\n const section = formatSection(\"Packages\", data.packages);\n section[0] = \"\";\n section[1] = chalk.bold(\"Packages:\");\n lines.push(...section);\n } else {\n lines.push(\"\");\n lines.push(chalk.yellow(\" No assistant-ui packages found.\"));\n }\n\n if (data.ecosystem.length > 0) {\n const section = formatSection(\"Ecosystem\", data.ecosystem);\n section[0] = \"\";\n section[1] = chalk.bold(\"Ecosystem:\");\n lines.push(...section);\n }\n\n if (data.warnings.length > 0) {\n lines.push(\"\");\n lines.push(chalk.yellow.bold(\"Warnings:\"));\n for (const w of data.warnings) {\n lines.push(chalk.yellow(` ! ${w}`));\n }\n }\n\n return lines;\n}\n\nexport const info = new Command()\n .name(\"info\")\n .description(\"Print environment and package information for bug reports.\")\n .option(\n \"-c, --cwd <cwd>\",\n \"the working directory. defaults to the current directory.\",\n process.cwd(),\n )\n .action(async (opts) => {\n const cwd = resolveRealPath(opts.cwd);\n const packageJsonPath = path.join(cwd, \"package.json\");\n\n if (!fs.existsSync(packageJsonPath)) {\n console.error(\n chalk.red(\"No package.json found in the current directory.\"),\n );\n process.exit(1);\n }\n\n const packageJsonContent = fs.readFileSync(packageJsonPath, \"utf8\");\n let projectPkg: Record<string, unknown>;\n try {\n projectPkg = JSON.parse(packageJsonContent);\n } catch {\n logPackageJsonParseError(packageJsonPath, \"info\");\n process.exit(1);\n }\n\n const data = await collectInfo(cwd, projectPkg);\n\n // Colored output for terminal\n console.log(\"\");\n for (const line of renderColored(data)) {\n console.log(line);\n }\n console.log(\"\");\n\n // Copyable plain text\n const plain = renderPlain(data);\n const block = [\"```\", ...plain, \"```\"].join(\"\\n\");\n\n console.log(chalk.dim(\"— Copy the text below into your bug report —\"));\n console.log(\"\");\n console.log(block);\n console.log(\"\");\n });\n"],"mappings":"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"}
package/dist/index.js CHANGED
File without changes
@@ -19,6 +19,7 @@ export declare const loginToCloud: (config: CloudAuthConfig, options?: {
19
19
  setupUrl?: string;
20
20
  print?: (text: string) => void;
21
21
  }) => Promise<DeviceCredentials>;
22
+ export declare const savedCloudAccessToken: (config: CloudAuthConfig) => Promise<string | null>;
22
23
  export declare const cloudAccessToken: (config: CloudAuthConfig, options?: {
23
24
  noOpen?: boolean;
24
25
  setupUrl?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"cloud-auth.d.ts","sourceRoot":"","sources":["../../src/lib/cloud-auth.ts"],"names":[],"mappings":"AAcA,OAAO,EAGL,KAAK,iBAAiB,EACvB,MAAM,iBAAiB,CAAC;AAIzB,OAAO,EAAE,gBAAgB,EAAE,uBAAoB;AAE/C,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,KAAK,gBAAgB,GAAG,iBAAiB,GAAG;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,eAAO,MAAM,eAAe,QAAO,eAWjC,CAAC;AAEH,eAAO,MAAM,oBAAoB,GAC/B,QAAQ,eAAe,EACvB,UAAS;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAO,KACtC,OAAO,CAAC,gBAAgB,GAAG,IAAI,CA8BjC,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,QAAQ,eAAe,EACvB,aAAa,iBAAiB,EAC9B,SAAS,WAAW,KACnB,OAAO,CAAC,IAAI,CA8Cd,CAAC;AAgBF,eAAO,MAAM,YAAY,GACvB,QAAQ,eAAe,EACvB,UAAS;IACP,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3B,KACL,OAAO,CAAC,iBAAiB,CA0E3B,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAC3B,QAAQ,eAAe,EACvB,UAAS;IACP,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3B,KACL,OAAO,CAAC,MAAM,CAoBhB,CAAC;AAEF,eAAO,MAAM,eAAe,GAC1B,QAAQ,eAAe,KACtB,OAAO,CAAC,IAAI,CASd,CAAC"}
1
+ {"version":3,"file":"cloud-auth.d.ts","sourceRoot":"","sources":["../../src/lib/cloud-auth.ts"],"names":[],"mappings":"AAcA,OAAO,EAGL,KAAK,iBAAiB,EACvB,MAAM,iBAAiB,CAAC;AAIzB,OAAO,EAAE,gBAAgB,EAAE,uBAAoB;AAE/C,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,KAAK,gBAAgB,GAAG,iBAAiB,GAAG;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,eAAO,MAAM,eAAe,QAAO,eAWjC,CAAC;AAEH,eAAO,MAAM,oBAAoB,GAC/B,QAAQ,eAAe,EACvB,UAAS;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAO,KACtC,OAAO,CAAC,gBAAgB,GAAG,IAAI,CA8BjC,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,QAAQ,eAAe,EACvB,aAAa,iBAAiB,EAC9B,SAAS,WAAW,KACnB,OAAO,CAAC,IAAI,CA8Cd,CAAC;AAgBF,eAAO,MAAM,YAAY,GACvB,QAAQ,eAAe,EACvB,UAAS;IACP,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3B,KACL,OAAO,CAAC,iBAAiB,CA0E3B,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,QAAQ,eAAe,KACtB,OAAO,CAAC,MAAM,GAAG,IAAI,CAoBvB,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAC3B,QAAQ,eAAe,EACvB,UAAS;IACP,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3B,KACL,OAAO,CAAC,MAAM,CAEyC,CAAC;AAE3D,eAAO,MAAM,eAAe,GAC1B,QAAQ,eAAe,KACtB,OAAO,CAAC,IAAI,CASd,CAAC"}
@@ -136,9 +136,9 @@ const loginToCloud = async (config, options = {}) => {
136
136
  await setup?.dispose();
137
137
  }
138
138
  };
139
- const cloudAccessToken = async (config, options = {}) => {
139
+ const savedCloudAccessToken = async (config) => {
140
140
  const saved = await readCloudCredentials(config);
141
- if (!saved) return (await loginToCloud(config, options)).tokens.accessToken;
141
+ if (!saved) return null;
142
142
  const login = createDeviceLogin({
143
143
  issuer: config.issuer,
144
144
  clientId: config.clientId
@@ -155,6 +155,7 @@ const cloudAccessToken = async (config, options = {}) => {
155
155
  throw error;
156
156
  }
157
157
  };
158
+ const cloudAccessToken = async (config, options = {}) => await savedCloudAccessToken(config) ?? (await loginToCloud(config, options)).tokens.accessToken;
158
159
  const logoutFromCloud = async (config) => {
159
160
  const saved = await readCloudCredentials(config, { matchConfig: false });
160
161
  if (saved) await createDeviceLogin({
@@ -164,6 +165,6 @@ const logoutFromCloud = async (config) => {
164
165
  await rm(config.credentialsFile, { force: true });
165
166
  };
166
167
  //#endregion
167
- export { cloudAccessToken, cloudAuthConfig, loginToCloud, logoutFromCloud, readCloudCredentials, saveCloudCredentials, validateCloudUrl };
168
+ export { cloudAccessToken, cloudAuthConfig, loginToCloud, logoutFromCloud, readCloudCredentials, saveCloudCredentials, savedCloudAccessToken, validateCloudUrl };
168
169
 
169
170
  //# sourceMappingURL=cloud-auth.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cloud-auth.js","names":[],"sources":["../../src/lib/cloud-auth.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport { renameSync, statSync, unlinkSync } from \"node:fs\";\nimport {\n chmod,\n link,\n mkdir,\n readFile,\n rename,\n rm,\n writeFile,\n} from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport {\n createDeviceLogin,\n isAccessDenied,\n type DeviceCredentials,\n} from \"aui-auth/device\";\nimport { abortable } from \"./cloud-abort\";\nimport { validateCloudUrl } from \"./cloud-url\";\n\nexport { validateCloudUrl } from \"./cloud-url\";\n\nexport type CloudAuthConfig = {\n issuer: string;\n clientId: string;\n credentialsFile: string;\n};\n\ntype SavedCredentials = DeviceCredentials & {\n issuer: string;\n clientId: string;\n};\n\nexport const cloudAuthConfig = (): CloudAuthConfig => ({\n issuer: validateCloudUrl(\n process.env.ASSISTANT_UI_ACCOUNTS_URL ??\n \"https://accounts.assistant-ui.com\",\n ),\n clientId: process.env.ASSISTANT_UI_OIDC_CLIENT_ID ?? \"assistant-ui-cli\",\n credentialsFile: path.join(\n process.env.ASSISTANT_UI_CONFIG_DIR ??\n path.join(os.homedir(), \".assistant-ui\"),\n \"credentials.json\",\n ),\n});\n\nexport const readCloudCredentials = async (\n config: CloudAuthConfig,\n options: { matchConfig?: boolean } = {},\n): Promise<SavedCredentials | null> => {\n let contents: string;\n try {\n contents = await readFile(config.credentialsFile, \"utf8\");\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw error;\n }\n let saved: SavedCredentials;\n try {\n saved = JSON.parse(contents) as SavedCredentials;\n } catch {\n throw new Error(\"Invalid saved login. Run assistant-ui cloud login again.\");\n }\n if (!saved || typeof saved !== \"object\")\n throw new Error(\"Invalid saved login. Run assistant-ui cloud login again.\");\n if (\n options.matchConfig !== false &&\n (saved.issuer !== config.issuer || saved.clientId !== config.clientId)\n ) {\n return null;\n }\n if (\n !saved.tokens?.accessToken ||\n !saved.user?.id ||\n typeof saved.tokens.accessTokenExpiresAt !== \"number\"\n ) {\n throw new Error(\"Invalid saved login. Run assistant-ui cloud login again.\");\n }\n return saved;\n};\n\nexport const saveCloudCredentials = async (\n config: CloudAuthConfig,\n credentials: DeviceCredentials,\n signal?: AbortSignal,\n): Promise<void> => {\n await mkdir(path.dirname(config.credentialsFile), {\n recursive: true,\n mode: 0o700,\n });\n const temporary = `${config.credentialsFile}.${randomUUID()}.tmp`;\n const backup = `${temporary}.previous`;\n let previous = false;\n let replacement: ReturnType<typeof statSync> | undefined;\n let renamed = false;\n try {\n try {\n await link(config.credentialsFile, backup);\n previous = true;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\n }\n await writeFile(\n temporary,\n `${JSON.stringify({ ...credentials, issuer: config.issuer, clientId: config.clientId }, null, 2)}\\n`,\n { mode: 0o600, flag: \"wx\" },\n );\n await chmod(temporary, 0o600);\n replacement = statSync(temporary);\n signal?.throwIfAborted();\n await rename(temporary, config.credentialsFile);\n renamed = true;\n signal?.throwIfAborted();\n } catch (error) {\n if (renamed && replacement) {\n let current: ReturnType<typeof statSync> | undefined;\n try {\n current = statSync(config.credentialsFile);\n } catch (failure) {\n if ((failure as NodeJS.ErrnoException).code !== \"ENOENT\") throw failure;\n }\n if (current?.dev === replacement.dev && current.ino === replacement.ino) {\n if (previous) renameSync(backup, config.credentialsFile);\n else unlinkSync(config.credentialsFile);\n }\n }\n throw error;\n } finally {\n await rm(temporary, { force: true });\n await rm(backup, { force: true });\n }\n};\n\nconst openBrowser = (url: string): void => {\n const command =\n process.platform === \"darwin\"\n ? \"open\"\n : process.platform === \"win32\"\n ? \"rundll32\"\n : \"xdg-open\";\n const args =\n process.platform === \"win32\" ? [\"url.dll,FileProtocolHandler\", url] : [url];\n const child = spawn(command, args, { detached: true, stdio: \"ignore\" });\n child.on(\"error\", () => {});\n child.unref();\n};\n\nexport const loginToCloud = async (\n config: CloudAuthConfig,\n options: {\n noOpen?: boolean;\n setupUrl?: string;\n print?: (text: string) => void;\n } = {},\n): Promise<DeviceCredentials> => {\n if (\n options.setupUrl &&\n ![\"assistant-ui-cli\", \"assistant-ui-cli-dev\"].includes(config.clientId)\n ) {\n throw new Error(\n \"Wizard sign-in requires the assistant-ui CLI OAuth client.\",\n );\n }\n const print = options.print ?? console.log;\n const setup = options.setupUrl\n ? await (\n await import(\"./cloud-setup-login\")\n ).connectCloudLoginSetup(options.setupUrl)\n : undefined;\n const signal = AbortSignal.any([\n AbortSignal.timeout(10 * 60 * 1000),\n ...(setup ? [setup.signal] : []),\n ]);\n const login = createDeviceLogin({\n issuer: config.issuer,\n clientId: config.clientId,\n fetch: (input, init) => {\n const url = new URL(input instanceof Request ? input.url : String(input));\n return fetch(\n input,\n url.pathname === \"/api/auth/device/code\"\n ? {\n ...init,\n signal: AbortSignal.any([\n signal,\n ...(init?.signal ? [init.signal] : []),\n ]),\n }\n : init,\n );\n },\n });\n let credentials: DeviceCredentials | undefined;\n let committed = false;\n try {\n signal.throwIfAborted();\n const authorization = await abortable(login.start(), signal);\n signal.throwIfAborted();\n const approvalUrl =\n (await setup?.publish(authorization, config.issuer)) ??\n authorization.verificationUriComplete;\n print(`Confirm code ${authorization.userCode} at ${approvalUrl}`);\n if (!options.noOpen && !setup)\n openBrowser(authorization.verificationUriComplete);\n credentials = await login.wait(authorization, { signal });\n signal.throwIfAborted();\n await saveCloudCredentials(config, credentials, signal);\n committed = true;\n await setup?.complete(\"signed-in\").catch(() => {\n print(\"Signed in locally. The setup connection could not be updated.\");\n });\n print(`Signed in as ${credentials.user.email || credentials.user.id}.`);\n return credentials;\n } catch (error) {\n if (credentials && !committed) {\n await login.revoke(credentials).catch(() => {\n print(\n \"The cancelled login could not be revoked. End it from your Accounts Sessions page.\",\n );\n });\n }\n await setup\n ?.complete(signal.aborted ? \"cancelled\" : \"failed\")\n .catch(() => {});\n throw error;\n } finally {\n await setup?.dispose();\n }\n};\n\nexport const cloudAccessToken = async (\n config: CloudAuthConfig,\n options: {\n noOpen?: boolean;\n setupUrl?: string;\n print?: (text: string) => void;\n } = {},\n): Promise<string> => {\n const saved = await readCloudCredentials(config);\n if (!saved) return (await loginToCloud(config, options)).tokens.accessToken;\n const login = createDeviceLogin({\n issuer: config.issuer,\n clientId: config.clientId,\n });\n try {\n const credentials = await login.ensureFresh(saved);\n if (credentials !== saved) await saveCloudCredentials(config, credentials);\n return credentials.tokens.accessToken;\n } catch (error) {\n if (isAccessDenied(error)) {\n await rm(config.credentialsFile, { force: true });\n throw new Error(\n \"Your login expired. Run assistant-ui cloud login again.\",\n );\n }\n throw error;\n }\n};\n\nexport const logoutFromCloud = async (\n config: CloudAuthConfig,\n): Promise<void> => {\n const saved = await readCloudCredentials(config, { matchConfig: false });\n if (saved) {\n await createDeviceLogin({\n issuer: validateCloudUrl(saved.issuer),\n clientId: saved.clientId,\n }).revoke(saved);\n }\n await rm(config.credentialsFile, { force: true });\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"cloud-auth.js","names":[],"sources":["../../src/lib/cloud-auth.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport { renameSync, statSync, unlinkSync } from \"node:fs\";\nimport {\n chmod,\n link,\n mkdir,\n readFile,\n rename,\n rm,\n writeFile,\n} from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport {\n createDeviceLogin,\n isAccessDenied,\n type DeviceCredentials,\n} from \"aui-auth/device\";\nimport { abortable } from \"./cloud-abort\";\nimport { validateCloudUrl } from \"./cloud-url\";\n\nexport { validateCloudUrl } from \"./cloud-url\";\n\nexport type CloudAuthConfig = {\n issuer: string;\n clientId: string;\n credentialsFile: string;\n};\n\ntype SavedCredentials = DeviceCredentials & {\n issuer: string;\n clientId: string;\n};\n\nexport const cloudAuthConfig = (): CloudAuthConfig => ({\n issuer: validateCloudUrl(\n process.env.ASSISTANT_UI_ACCOUNTS_URL ??\n \"https://accounts.assistant-ui.com\",\n ),\n clientId: process.env.ASSISTANT_UI_OIDC_CLIENT_ID ?? \"assistant-ui-cli\",\n credentialsFile: path.join(\n process.env.ASSISTANT_UI_CONFIG_DIR ??\n path.join(os.homedir(), \".assistant-ui\"),\n \"credentials.json\",\n ),\n});\n\nexport const readCloudCredentials = async (\n config: CloudAuthConfig,\n options: { matchConfig?: boolean } = {},\n): Promise<SavedCredentials | null> => {\n let contents: string;\n try {\n contents = await readFile(config.credentialsFile, \"utf8\");\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw error;\n }\n let saved: SavedCredentials;\n try {\n saved = JSON.parse(contents) as SavedCredentials;\n } catch {\n throw new Error(\"Invalid saved login. Run assistant-ui cloud login again.\");\n }\n if (!saved || typeof saved !== \"object\")\n throw new Error(\"Invalid saved login. Run assistant-ui cloud login again.\");\n if (\n options.matchConfig !== false &&\n (saved.issuer !== config.issuer || saved.clientId !== config.clientId)\n ) {\n return null;\n }\n if (\n !saved.tokens?.accessToken ||\n !saved.user?.id ||\n typeof saved.tokens.accessTokenExpiresAt !== \"number\"\n ) {\n throw new Error(\"Invalid saved login. Run assistant-ui cloud login again.\");\n }\n return saved;\n};\n\nexport const saveCloudCredentials = async (\n config: CloudAuthConfig,\n credentials: DeviceCredentials,\n signal?: AbortSignal,\n): Promise<void> => {\n await mkdir(path.dirname(config.credentialsFile), {\n recursive: true,\n mode: 0o700,\n });\n const temporary = `${config.credentialsFile}.${randomUUID()}.tmp`;\n const backup = `${temporary}.previous`;\n let previous = false;\n let replacement: ReturnType<typeof statSync> | undefined;\n let renamed = false;\n try {\n try {\n await link(config.credentialsFile, backup);\n previous = true;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\n }\n await writeFile(\n temporary,\n `${JSON.stringify({ ...credentials, issuer: config.issuer, clientId: config.clientId }, null, 2)}\\n`,\n { mode: 0o600, flag: \"wx\" },\n );\n await chmod(temporary, 0o600);\n replacement = statSync(temporary);\n signal?.throwIfAborted();\n await rename(temporary, config.credentialsFile);\n renamed = true;\n signal?.throwIfAborted();\n } catch (error) {\n if (renamed && replacement) {\n let current: ReturnType<typeof statSync> | undefined;\n try {\n current = statSync(config.credentialsFile);\n } catch (failure) {\n if ((failure as NodeJS.ErrnoException).code !== \"ENOENT\") throw failure;\n }\n if (current?.dev === replacement.dev && current.ino === replacement.ino) {\n if (previous) renameSync(backup, config.credentialsFile);\n else unlinkSync(config.credentialsFile);\n }\n }\n throw error;\n } finally {\n await rm(temporary, { force: true });\n await rm(backup, { force: true });\n }\n};\n\nconst openBrowser = (url: string): void => {\n const command =\n process.platform === \"darwin\"\n ? \"open\"\n : process.platform === \"win32\"\n ? \"rundll32\"\n : \"xdg-open\";\n const args =\n process.platform === \"win32\" ? [\"url.dll,FileProtocolHandler\", url] : [url];\n const child = spawn(command, args, { detached: true, stdio: \"ignore\" });\n child.on(\"error\", () => {});\n child.unref();\n};\n\nexport const loginToCloud = async (\n config: CloudAuthConfig,\n options: {\n noOpen?: boolean;\n setupUrl?: string;\n print?: (text: string) => void;\n } = {},\n): Promise<DeviceCredentials> => {\n if (\n options.setupUrl &&\n ![\"assistant-ui-cli\", \"assistant-ui-cli-dev\"].includes(config.clientId)\n ) {\n throw new Error(\n \"Wizard sign-in requires the assistant-ui CLI OAuth client.\",\n );\n }\n const print = options.print ?? console.log;\n const setup = options.setupUrl\n ? await (\n await import(\"./cloud-setup-login\")\n ).connectCloudLoginSetup(options.setupUrl)\n : undefined;\n const signal = AbortSignal.any([\n AbortSignal.timeout(10 * 60 * 1000),\n ...(setup ? [setup.signal] : []),\n ]);\n const login = createDeviceLogin({\n issuer: config.issuer,\n clientId: config.clientId,\n fetch: (input, init) => {\n const url = new URL(input instanceof Request ? input.url : String(input));\n return fetch(\n input,\n url.pathname === \"/api/auth/device/code\"\n ? {\n ...init,\n signal: AbortSignal.any([\n signal,\n ...(init?.signal ? [init.signal] : []),\n ]),\n }\n : init,\n );\n },\n });\n let credentials: DeviceCredentials | undefined;\n let committed = false;\n try {\n signal.throwIfAborted();\n const authorization = await abortable(login.start(), signal);\n signal.throwIfAborted();\n const approvalUrl =\n (await setup?.publish(authorization, config.issuer)) ??\n authorization.verificationUriComplete;\n print(`Confirm code ${authorization.userCode} at ${approvalUrl}`);\n if (!options.noOpen && !setup)\n openBrowser(authorization.verificationUriComplete);\n credentials = await login.wait(authorization, { signal });\n signal.throwIfAborted();\n await saveCloudCredentials(config, credentials, signal);\n committed = true;\n await setup?.complete(\"signed-in\").catch(() => {\n print(\"Signed in locally. The setup connection could not be updated.\");\n });\n print(`Signed in as ${credentials.user.email || credentials.user.id}.`);\n return credentials;\n } catch (error) {\n if (credentials && !committed) {\n await login.revoke(credentials).catch(() => {\n print(\n \"The cancelled login could not be revoked. End it from your Accounts Sessions page.\",\n );\n });\n }\n await setup\n ?.complete(signal.aborted ? \"cancelled\" : \"failed\")\n .catch(() => {});\n throw error;\n } finally {\n await setup?.dispose();\n }\n};\n\nexport const savedCloudAccessToken = async (\n config: CloudAuthConfig,\n): Promise<string | null> => {\n const saved = await readCloudCredentials(config);\n if (!saved) return null;\n const login = createDeviceLogin({\n issuer: config.issuer,\n clientId: config.clientId,\n });\n try {\n const credentials = await login.ensureFresh(saved);\n if (credentials !== saved) await saveCloudCredentials(config, credentials);\n return credentials.tokens.accessToken;\n } catch (error) {\n if (isAccessDenied(error)) {\n await rm(config.credentialsFile, { force: true });\n throw new Error(\n \"Your login expired. Run assistant-ui cloud login again.\",\n );\n }\n throw error;\n }\n};\n\nexport const cloudAccessToken = async (\n config: CloudAuthConfig,\n options: {\n noOpen?: boolean;\n setupUrl?: string;\n print?: (text: string) => void;\n } = {},\n): Promise<string> =>\n (await savedCloudAccessToken(config)) ??\n (await loginToCloud(config, options)).tokens.accessToken;\n\nexport const logoutFromCloud = async (\n config: CloudAuthConfig,\n): Promise<void> => {\n const saved = await readCloudCredentials(config, { matchConfig: false });\n if (saved) {\n await createDeviceLogin({\n issuer: validateCloudUrl(saved.issuer),\n clientId: saved.clientId,\n }).revoke(saved);\n }\n await rm(config.credentialsFile, { force: true });\n};\n"],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ export declare const CLOUD_CLI_RUN_PATH = "/api/cli/run";
2
+ export type CloudCliAction = {
3
+ type: string;
4
+ [key: string]: unknown;
5
+ };
6
+ export type CloudCliResponse = {
7
+ stdout?: string;
8
+ stderr?: string;
9
+ exitCode: number;
10
+ actions?: CloudCliAction[];
11
+ };
12
+ export declare const joinCloudArgs: (args: readonly string[]) => string;
13
+ export declare const parseCloudCliResponse: (value: unknown) => CloudCliResponse | null;
14
+ export declare const sendCloudCommand: (cloudUrl: string, args: readonly string[], accessToken: string | null) => Promise<CloudCliResponse>;
15
+ export declare const runCloudCommand: (args: readonly string[], deps: {
16
+ cloudUrl: string;
17
+ accessToken: () => Promise<string | null>;
18
+ stdout: (text: string) => void;
19
+ stderr: (text: string) => void;
20
+ send?: typeof sendCloudCommand;
21
+ }) => Promise<number>;
22
+ //# sourceMappingURL=cloud-remote.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloud-remote.d.ts","sourceRoot":"","sources":["../../src/lib/cloud-remote.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,kBAAkB,iBAAiB,CAAC;AAEjD,MAAM,MAAM,cAAc,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CAAC;AAEtE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,cAAc,EAAE,CAAC;CAC5B,CAAC;AAIF,eAAO,MAAM,aAAa,GAAI,MAAM,SAAS,MAAM,EAAE,KAAG,MAG1C,CAAC;AAEf,eAAO,MAAM,qBAAqB,GAChC,OAAO,OAAO,KACb,gBAAgB,GAAG,IA+BrB,CAAC;AAOF,eAAO,MAAM,gBAAgB,GAC3B,UAAU,MAAM,EAChB,MAAM,SAAS,MAAM,EAAE,EACvB,aAAa,MAAM,GAAG,IAAI,KACzB,OAAO,CAAC,gBAAgB,CAkC1B,CAAC;AASF,eAAO,MAAM,eAAe,GAC1B,MAAM,SAAS,MAAM,EAAE,EACvB,MAAM;IACJ,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC1C,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,IAAI,CAAC,EAAE,OAAO,gBAAgB,CAAC;CAChC,KACA,OAAO,CAAC,MAAM,CAiChB,CAAC"}