@pippit-dev/cli 1.0.38 → 1.0.40

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 (45) hide show
  1. package/README.md +84 -1
  2. package/checksums.txt +6 -6
  3. package/cmd/auth/auth.go +4 -3
  4. package/cmd/marketing.go +5 -4
  5. package/cmd/marketing_test.go +58 -0
  6. package/cmd/root.go +40 -14
  7. package/cmd/root_test.go +63 -0
  8. package/cmd/source_test.go +2 -3
  9. package/cmd/submit_run_test.go +20 -81
  10. package/cmd/update/update.go +36 -10
  11. package/cmd/update/update_test.go +73 -3
  12. package/dist/checksums.txt +6 -6
  13. package/dist/xyq-canvas-command-runtime.cjs +14 -14
  14. package/dist/xyq-canvas-command-runtime.cjs.LEGAL.txt +0 -206
  15. package/dist/xyq-canvas-command-runtime.cjs.sha256 +2 -2
  16. package/internal/adapter/adapter.go +73 -0
  17. package/internal/adapter/ppe.go +36 -0
  18. package/internal/adaptercli/commands.go +27 -0
  19. package/internal/adaptercli/commands_test.go +41 -0
  20. package/internal/auth/manager.go +25 -13
  21. package/internal/canvas/apply.go +3 -0
  22. package/internal/canvas/media_identity.go +300 -0
  23. package/internal/canvas/media_identity_test.go +160 -0
  24. package/internal/canvas/upload.go +30 -1
  25. package/internal/commandnames/names.go +9 -0
  26. package/internal/common/adapter_test.go +74 -0
  27. package/internal/common/client.go +37 -12
  28. package/internal/config/config.go +41 -0
  29. package/internal/config/config_test.go +41 -1
  30. package/internal/generate_video/generate_video.go +1 -0
  31. package/internal/generate_video/generate_video_test.go +25 -3
  32. package/internal/models/models.go +6 -2
  33. package/internal/models/models_test.go +7 -0
  34. package/package.json +2 -2
  35. package/scripts/canvas-command.js +4 -2
  36. package/skills/short-drama/SKILL.md +6 -3
  37. package/skills/short-drama/commands/download-result.md +9 -3
  38. package/skills/short-drama/commands/get-thread.md +2 -0
  39. package/skills/short-drama/commands/list-thread-file.md +12 -6
  40. package/skills/short-drama/commands/submit-run.md +3 -1
  41. package/skills/short-drama/examples/create-and-deliver.md +2 -2
  42. package/skills/short-drama/examples/reference-and-continue.md +20 -1
  43. package/skills/short-drama/workflows/creation.md +28 -14
  44. package/skills/short-drama/workflows/poll-and-deliver.md +27 -9
  45. package/skills/xyq-nest-skill/commands/canvas-assets.md +9 -1
@@ -14,7 +14,9 @@ import (
14
14
  "sync"
15
15
  "time"
16
16
 
17
+ "github.com/Pippit-dev/pippit-cli/internal/adapter"
17
18
  "github.com/Pippit-dev/pippit-cli/internal/auth"
19
+ "github.com/Pippit-dev/pippit-cli/internal/commandnames"
18
20
  "github.com/Pippit-dev/pippit-cli/internal/version"
19
21
  "github.com/spf13/cobra"
20
22
  )
@@ -38,29 +40,53 @@ var telemetrySkillNames = []string{
38
40
  "xyq-short-drama-skill",
39
41
  }
40
42
 
41
- var telemetryHTTPClient = &http.Client{Timeout: telemetryWaitTimeout}
43
+ // Each update command instance owns its HTTP client so instances using different adapters do not affect each other.
44
+ type updater struct{ telemetryHTTPClient *http.Client }
45
+
46
+ func newUpdater(policy adapter.HTTPAdapter) *updater {
47
+ return &updater{telemetryHTTPClient: &http.Client{
48
+ Timeout: telemetryWaitTimeout,
49
+ Transport: adapter.HTTPOrDefault(policy).WrapTransport(nil),
50
+ }}
51
+ }
42
52
 
43
53
  // NewCommand builds the update command.
44
54
  func NewCommand(stdout, stderr io.Writer) *cobra.Command {
55
+ return NewCommandWithAdapter(stdout, stderr, adapter.Default{})
56
+ }
57
+
58
+ func NewCommandWithAdapter(stdout, stderr io.Writer, policy adapter.HTTPAdapter) *cobra.Command {
59
+ updater := newUpdater(policy)
45
60
  var source string
61
+ var channel string
46
62
  cmd := &cobra.Command{
47
- Use: "update",
63
+ Use: commandnames.Update,
48
64
  Short: "Update pippit-tool-cli and bundled skills",
49
65
  RunE: func(cmd *cobra.Command, _ []string) error {
50
66
  if !cmd.Flags().Changed("source") {
51
67
  source = os.Getenv("PIPPIT_CLI_SOURCE")
52
68
  }
53
- return runUpdate(stdout, stderr, source)
69
+ return updater.runUpdate(stdout, stderr, source, channel)
54
70
  },
55
71
  }
56
72
  cmd.Flags().StringVar(&source, "source", "", "optional real host identifier reported as host_platform; defaults to PIPPIT_CLI_SOURCE when unset")
73
+ cmd.Flags().StringVar(&channel, "channel", "", "channel for this update only: stable (npm latest) or canary; overrides PIPPIT_CLI_INSTALL_PACKAGE")
57
74
  return cmd
58
75
  }
59
76
 
60
- func runUpdate(stdout, stderr io.Writer, source string) error {
77
+ func (u *updater) runUpdate(stdout, stderr io.Writer, hostPlatform, channel string) error {
61
78
  pkg := os.Getenv("PIPPIT_CLI_INSTALL_PACKAGE")
62
- if pkg == "" {
79
+ switch channel {
80
+ case "":
81
+ if pkg == "" {
82
+ pkg = defaultPackage + "@latest"
83
+ }
84
+ case "stable":
63
85
  pkg = defaultPackage + "@latest"
86
+ case "canary":
87
+ pkg = defaultPackage + "@canary"
88
+ default:
89
+ return fmt.Errorf("不支持的更新渠道 %q,可选值为 stable 或 canary", channel)
64
90
  }
65
91
 
66
92
  fmt.Fprintf(stderr, "Updating pippit-tool-cli via npm: %s\n", pkg)
@@ -83,7 +109,7 @@ func runUpdate(stdout, stderr io.Writer, source string) error {
83
109
  return fmt.Errorf("更新 pippit-tool-cli skills 失败: %w", err)
84
110
  }
85
111
 
86
- reportBundledSkillTelemetry("update", "cli_update", source, stderr)
112
+ u.reportBundledSkillTelemetry("update", "cli_update", hostPlatform, stderr)
87
113
  fmt.Fprintln(stdout, "pippit-tool-cli and skills updated")
88
114
  return nil
89
115
  }
@@ -143,7 +169,7 @@ type telemetryPayload struct {
143
169
  Arch string `json:"arch"`
144
170
  }
145
171
 
146
- func reportBundledSkillTelemetry(event, source, hostPlatform string, stderr io.Writer) {
172
+ func (u *updater) reportBundledSkillTelemetry(event, source, hostPlatform string, stderr io.Writer) {
147
173
  if os.Getenv("PIPPIT_CLI_DISABLE_TELEMETRY") == "1" {
148
174
  return
149
175
  }
@@ -161,7 +187,7 @@ func reportBundledSkillTelemetry(event, source, hostPlatform string, stderr io.W
161
187
  wg.Add(1)
162
188
  go func(payload telemetryPayload) {
163
189
  defer wg.Done()
164
- if err := reportSkillTelemetry(payload); err != nil && os.Getenv("PIPPIT_CLI_DEBUG_TELEMETRY") == "1" {
190
+ if err := u.reportSkillTelemetry(payload); err != nil && os.Getenv("PIPPIT_CLI_DEBUG_TELEMETRY") == "1" {
165
191
  fmt.Fprintf(stderr, "[pippit-tool-cli] 埋点上报失败: %v\n", err)
166
192
  }
167
193
  }(payload)
@@ -177,7 +203,7 @@ func reportBundledSkillTelemetry(event, source, hostPlatform string, stderr io.W
177
203
  }
178
204
  }
179
205
 
180
- func reportSkillTelemetry(payload telemetryPayload) error {
206
+ func (u *updater) reportSkillTelemetry(payload telemetryPayload) error {
181
207
  body, err := json.Marshal(payload)
182
208
  if err != nil {
183
209
  return err
@@ -190,7 +216,7 @@ func reportSkillTelemetry(payload telemetryPayload) error {
190
216
  req.Header.Set("Content-Type", "application/json")
191
217
  req.Header.Set("Authorization", telemetryAuthHeader)
192
218
 
193
- resp, err := telemetryHTTPClient.Do(req)
219
+ resp, err := u.telemetryHTTPClient.Do(req)
194
220
  if err != nil {
195
221
  return err
196
222
  }
@@ -11,8 +11,78 @@ import (
11
11
  "strings"
12
12
  "testing"
13
13
  "time"
14
+
15
+ "github.com/Pippit-dev/pippit-cli/internal/adapter"
14
16
  )
15
17
 
18
+ func TestUpdateCommandSelectsChannel(t *testing.T) {
19
+ if runtime.GOOS == "windows" {
20
+ t.Skip("test uses POSIX shell scripts to stub npm and npx")
21
+ }
22
+ cases := []struct {
23
+ name string
24
+ args []string
25
+ override string
26
+ want string
27
+ wantErr bool
28
+ }{
29
+ {name: "default stable", want: defaultPackage + "@latest"},
30
+ {name: "explicit stable", args: []string{"--channel", "stable"}, want: defaultPackage + "@latest"},
31
+ {name: "explicit canary", args: []string{"--channel", "canary"}, want: defaultPackage + "@canary"},
32
+ {name: "package override preserved", override: defaultPackage + "@1.0.38", want: defaultPackage + "@1.0.38"},
33
+ {name: "stable overrides environment", args: []string{"--channel", "stable"}, override: defaultPackage + "@canary", want: defaultPackage + "@latest"},
34
+ {name: "canary overrides environment", args: []string{"--channel", "canary"}, override: defaultPackage + "@1.0.38", want: defaultPackage + "@canary"},
35
+ {name: "invalid channel", args: []string{"--channel", "beta"}, wantErr: true},
36
+ {name: "invalid channel with override", args: []string{"--channel", "beta"}, override: defaultPackage + "@1.0.38", wantErr: true},
37
+ }
38
+ for _, tc := range cases {
39
+ t.Run(tc.name, func(t *testing.T) {
40
+ binDir := t.TempDir()
41
+ npmRoot := t.TempDir()
42
+ capturePath := filepath.Join(t.TempDir(), "npm-args.txt")
43
+ if err := os.MkdirAll(filepath.Join(npmRoot, "@pippit-dev", "cli", "skills"), 0o755); err != nil {
44
+ t.Fatal(err)
45
+ }
46
+ npmScript := "#!/bin/sh\ncase \"$1\" in\ninstall) printf '%s\\n' \"$@\" > \"$CAPTURE_ARGS\";;\nroot) printf '%s\\n' \"$TEST_NPM_ROOT\";;\n*) exit 1;;\nesac\n"
47
+ for name, script := range map[string]string{"npm": npmScript, "npx": "#!/bin/sh\nexit 0\n"} {
48
+ if err := os.WriteFile(filepath.Join(binDir, name), []byte(script), 0o755); err != nil {
49
+ t.Fatal(err)
50
+ }
51
+ }
52
+ t.Setenv("PATH", binDir+string(os.PathListSeparator)+os.Getenv("PATH"))
53
+ t.Setenv("HOME", t.TempDir())
54
+ t.Setenv("CAPTURE_ARGS", capturePath)
55
+ t.Setenv("TEST_NPM_ROOT", npmRoot)
56
+ t.Setenv("PIPPIT_CLI_DISABLE_TELEMETRY", "1")
57
+ t.Setenv("PIPPIT_CLI_INSTALL_PACKAGE", tc.override)
58
+ var stdout, stderr bytes.Buffer
59
+ cmd := NewCommand(&stdout, &stderr)
60
+ cmd.SetArgs(append([]string{}, tc.args...))
61
+ err := cmd.Execute()
62
+ if tc.wantErr {
63
+ if err == nil || !strings.Contains(err.Error(), "不支持的更新渠道") {
64
+ t.Fatalf("error = %v, want invalid channel error", err)
65
+ }
66
+ if _, err := os.Stat(capturePath); !os.IsNotExist(err) {
67
+ t.Fatalf("invalid channel invoked npm or stat failed: %v", err)
68
+ }
69
+ return
70
+ }
71
+ if err != nil {
72
+ t.Fatalf("update failed: %v, stderr: %s", err, stderr.String())
73
+ }
74
+ got, err := os.ReadFile(capturePath)
75
+ if err != nil {
76
+ t.Fatal(err)
77
+ }
78
+ want := strings.Join([]string{"install", "-g", "--prefer-online", "--foreground-scripts", tc.want, ""}, "\n")
79
+ if string(got) != want {
80
+ t.Fatalf("npm args = %q, want %q", got, want)
81
+ }
82
+ })
83
+ }
84
+ }
85
+
16
86
  func TestInstallSkillsInstallsAllBundledSkills(t *testing.T) {
17
87
  if runtime.GOOS == "windows" {
18
88
  t.Skip("test uses a POSIX shell script to stub npx")
@@ -92,7 +162,7 @@ func TestReportSkillTelemetry(t *testing.T) {
92
162
  defer server.Close()
93
163
 
94
164
  t.Setenv("PIPPIT_CLI_TELEMETRY_BASE_URL", server.URL+"/")
95
- err := reportSkillTelemetry(telemetryPayload{
165
+ err := newUpdater(adapter.Default{}).reportSkillTelemetry(telemetryPayload{
96
166
  Event: "update",
97
167
  SkillName: "xyq-skill",
98
168
  Source: "cli_update",
@@ -121,7 +191,7 @@ func TestReportBundledSkillTelemetryWaitsBriefly(t *testing.T) {
121
191
 
122
192
  t.Setenv("PIPPIT_CLI_TELEMETRY_BASE_URL", server.URL)
123
193
  start := time.Now()
124
- reportBundledSkillTelemetry("update", "cli_update", " workbuddy ", &bytes.Buffer{})
194
+ newUpdater(adapter.Default{}).reportBundledSkillTelemetry("update", "cli_update", " workbuddy ", &bytes.Buffer{})
125
195
  if elapsed := time.Since(start); elapsed > 1500*time.Millisecond {
126
196
  t.Fatalf("reportBundledSkillTelemetry() blocked for %v, want <= 1.5s", elapsed)
127
197
  }
@@ -143,7 +213,7 @@ func TestReportBundledSkillTelemetryReportsBothSkills(t *testing.T) {
143
213
  defer server.Close()
144
214
 
145
215
  t.Setenv("PIPPIT_CLI_TELEMETRY_BASE_URL", server.URL)
146
- reportBundledSkillTelemetry("update", "cli_update", " workbuddy ", &bytes.Buffer{})
216
+ newUpdater(adapter.Default{}).reportBundledSkillTelemetry("update", "cli_update", " workbuddy ", &bytes.Buffer{})
147
217
 
148
218
  got := map[string]bool{}
149
219
  for i := 0; i < len(telemetrySkillNames); i++ {
@@ -1,6 +1,6 @@
1
- 3fca50bc9ffb2c14e74ea65781fb706d4fa2c79484d5b20b3c08ad15847afc30 pippit-tool-cli-1.0.38-darwin-amd64.tar.gz
2
- 0e7d976179d944a2b8f99800acb91c6d3c035f0920cf4ec4348010b3a03d9e38 pippit-tool-cli-1.0.38-darwin-arm64.tar.gz
3
- 4f5723713fc910121982885172079ab1e787d2af9f2515961e59f7e53ba48737 pippit-tool-cli-1.0.38-linux-amd64.tar.gz
4
- 07d8ff5920dfa6748ad46076fcffbabc7b5d8dcef6714d802c7fa5b4b515494e pippit-tool-cli-1.0.38-linux-arm64.tar.gz
5
- 61f18a00f457ffff80c20593b3c0af84b93a3e8db871b00ed043f61024c6b441 pippit-tool-cli-1.0.38-windows-amd64.zip
6
- c07399e11956f7c6565e00c74a24f864012613539a61341636f9900d85a72654 pippit-tool-cli-1.0.38-windows-arm64.zip
1
+ 14eec459ef3d526057582d47ec5611a48146b825181f06652f31c76e203a7f9c pippit-tool-cli-1.0.40-darwin-amd64.tar.gz
2
+ 81d6967ef44953e91d9a2504bba20979570de7c0554aae4d7b55660da9fb32e8 pippit-tool-cli-1.0.40-darwin-arm64.tar.gz
3
+ 8811832e3c840cb0418654953f354b254f3889346966c161ab65279f0cf03662 pippit-tool-cli-1.0.40-linux-amd64.tar.gz
4
+ 28ce9f374f34fed4fa3e7b44a2b1b1fcc59c8cd7d83ad7621e8c694dd35194ad pippit-tool-cli-1.0.40-linux-arm64.tar.gz
5
+ ffd11a5de082b50b54d8b9f701890186c28785bca4cfd6e8e05254ce6eed965c pippit-tool-cli-1.0.40-windows-amd64.zip
6
+ 88ed4320bbef1146355890753226ec6a99b0f0653556e1a4665ad7f23dd17c30 pippit-tool-cli-1.0.40-windows-arm64.zip